/*
Copyright (c) [2019年5月1日] [吴超]
[MBT_Studio] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
#include "ui节点视口.h"
#include <core/几何计算/凸包计算.h>
#include <字符串数值互换.h>
#include <随机数.h>
#include <core/geometry.h>



//#include "视口元素计算/线操作计算.h"
#include "节点视口/拖放数据创建节点.h"
#include "物体编辑/3D控制手柄.h"
#include "工作区.h"

//#include <ppl.h>

using namespace std;



typedef uint32 t_遮罩数据类型;

typedef struct {
	uvec2     顶点区间;
	S_RGBA8UI 区颜色;
}S_凸包顶点区间和颜色;


//static S_连接线相交计算*	g连接线交叉计算 = 0;
static S_设备环境*			g计算环境 = 0;
static S_板载缓存*			g剪线焦点 = 0;




DEF_单色绘制ID(边焦点);
DEF_文本绘制ID(面板名称);
DEF_文本绘制ID(插座名称);

//DEF_文本绘制ID()
#define DEF_剪刀线状态_准备 0B00000001
#define DEF_剪刀线状态_开始 0B00000010
#define DEF_剪刀线状态_结束 0B00000000
static uint8 g剪刀线状态_状态 = DEF_剪刀线状态_结束;


static uint32 g面板绘制属性ID = 0;

static vec2			g纵向排列插座大小 = { 12, 12 };
static vec2			g纵向排列左边插座大小 = { 4,8 };


static uint32			g插座文本最大数 = 16;
static S_UI创建环境*	g_ctx;

static uint8 字符跨度 = 32;



static bool g开始移动视口			= false;
static bool g移动选中节点			= false;
static bool g打开选框绘制			= false;
static vec2 g节点鼠标局部位置		= { 0,0 };
static vec2 g节点视口鼠标局部位置	= { 0,0 };
static uint32 g每个面板元素数量 = 5;



static std::map<C_节点树*, std::set<C_节点视口*>>		g节点视口;
static float32					g节点面板圆角半径	= 6;
static C_节点视口*				g当前操作节点视口	= nullptr;
static uint32					g当前节点添加到树_树类型标示 = 0xffffffff;

static std::set<C_节点树*>		g返回菜单以记录节点树;
//static uint8 g剪断连接线 = 0;

static C_节点操作_移动节点* g当前移动面板操作 = nullptr;
static C_节点操作_删除节点* g当前删除面板操作 = nullptr;


C_Widget* C_节点视口::g多边形节点创建菜单 = nullptr;
C_Widget* C_节点视口::g函数节点创建菜单 = nullptr;
C_Widget* C_节点视口::g物体节点创建菜单 = nullptr;
C_Widget* C_节点视口::g材质节点创建菜单 = nullptr;
C_Widget* C_节点视口::g渲染节点创建菜单 = nullptr;
C_Widget* C_节点视口::g着色器节点创建菜单 = nullptr;

C_Widget* C_节点视口::g节点层级切换面板 = nullptr;



static uint32 f_收集节点凸包顶点(C_节点视口* 视口, C_节点面板* panel, std::vector<mat2x4>& 顶点, std::vector<S_凸包顶点区间和颜色>& 凸包顶点区间) {
	S_凸包顶点区间和颜色 区间;
	区间.顶点区间.x = 顶点.size()*4;
	区间.顶点区间.y = (panel->m_NodeData->m_区域内节点.size() + 1) * 4;
	

	//auto& r = 凸包顶点区间.back();

	mat2x4 point;
	f_NodePanel_get边界框(panel, point);
	顶点.push_back(point);

	if (panel->m_NodeData->m_区域内节点.size()) {
		for (auto& node : panel->m_NodeData->m_区域内节点) {
			for (auto& 区域内面板 : node->m_包含在多个视口的UI部件) {
				if (区域内面板->m_父部件 == 视口) {
					f_NodePanel_get边界框((C_节点面板*)区域内面板, point);
					顶点.push_back(point);
					break;
				}
			}
		}

		for (auto& node : panel->m_NodeData->m_区域内节点) {
			for (auto& 区域内面板 : node->m_包含在多个视口的UI部件) {
				if (区域内面板->m_父部件 == 视口) {
					switch (node->m_Type) {
						case E_节点类型::e_节点Type_循环迭代:
						case E_节点类型::e_节点Type_判断:
						case E_节点类型::e_节点Type_迭代:
						case E_节点类型::e_节点Type_循环: {
							区间.顶点区间.y += f_收集节点凸包顶点(视口, (C_节点面板*)区域内面板, 顶点, 凸包顶点区间);
							break;
						}
					}
					break;
				}
			}
		}

	}
	
	switch (panel->m_NodeData->m_Type) {
		case E_节点类型::e_节点Type_判断: 
			区间.区颜色 = { 0, 150, 200, 10 }; 
			break;
		case E_节点类型::e_节点Type_循环迭代:
		case E_节点类型::e_节点Type_迭代:
		case E_节点类型::e_节点Type_循环:
			区间.区颜色 = { 220, 120, 0, 10 }; 
			break;
	}
	凸包顶点区间.push_back({区间});

	return 区间.顶点区间.y;
}

static void f_设置当前操作节点视口(C_节点视口* view) {
	g当前操作节点视口 = view;

	switch (view->m_节点树后端->m_树类型) {
	case E_节点树类型::e_type_物体节点树:
		S_节点数据::G当前物体节点树 = view->m_节点树后端;
		break;
	case E_节点树类型::e_type_材质节点树: {
		S_节点数据::G当前材质节点树 = view->m_节点树后端;
		break;
	}
	case E_节点树类型::e_type_着色节点树: {
		S_节点数据::G当前材质节点树 = view->m_节点树后端;
		break;
	}
	case E_节点树类型::e_type_渲染节点树: {
		S_节点数据::G当前渲染节点树 = view->m_节点树后端;
		break;
	}
	case E_节点树类型::e_type_包节点树: {
		S_节点数据::G当前包节点树 = view->m_节点树后端;
		break;
	}
	}
}


/*************************************** 焦点处理 ********************************************/
static void f_节点获取焦点(C_节点面板** nodes, uint32 num) {
	//====================== 数据保存 ============================
	f_projectFile_添加焦点更新节点(nodes, num);
	


	std::vector<C_节点基类*> 节点面板集;
	for (uint32 i = 0; i < num; ++i) {
		for (auto& node : nodes[i]->m_NodeData->m_包含在多个视口的UI部件) {
			static_cast<C_节点面板*>(node)->f_设置焦点(true);
		}
		节点面板集.push_back(nodes[i]->m_NodeData);
	}

	
	f_global_clear激活节点();
	f_global_set激活物体(节点面板集);
	f_global_set激活节点(节点面板集);

	for (uint32 i = 0; i < num; ++i) {
		C_节点视口* view = static_cast<C_节点视口*>(nodes[i]->m_节点当前所在视口);
		nodes[i]->m_属性面板参数构建(view->m_属性栏, (S_结构对象指针)nodes[i]);
		f_widget_单个部件裁剪(nodes[i], view->m_裁剪大小, view->m_GlobalLoc);
	}


	for (auto& camera : S_框架::g_视口相机) {
		f_camera_set偏移视点(camera, f_global_get激活物体中心点());
	}
	
	f_页面_更新联动工作区(E_工作区类型::e_节点视口);

}



/***************************** 插座 ***************************/

static Inline vec2 f_node_get输入插座文本坐标(C_节点面板* plane, uint32 插座ID, uint16 字号) {
	vec2 coord = plane->f_getOriginalPos();
	coord.x += plane->f_getInSocket(插座ID)->f_getOriginalPos().x;
	
	coord.y += plane->f_getOriginalSize().y * 0.5;
	coord.y -= plane->m_内部面板->f_getOriginalSize().y * 0.5;

	coord.y -= C_节点面板::m_链接点直径 + 2;
	//coord.y -= plane->m_内部面板->f_getOriginalSize().y * 0.5;
	coord.y -= 字号 / plane->m_Scale.x;

	return coord;
}

static Inline vec2 f_node_get输出插座文本坐标(C_节点面板* plane, uint32 插座ID) {
	vec2 coord = plane->f_getOriginalPos();
	coord.x += plane->f_getOutSocket(插座ID)->f_getOriginalPos().x;
	coord.y += plane->f_getOutSocket(插座ID)->f_getOriginalPos().y;

	coord.y += C_节点面板::m_链接点直径 + 2;

	return coord;
}







static void f_节点平移(C_节点视口* 节点视口) {
	auto ws = 节点视口->mf_get更新组件(节点视口);
	//auto* 要更新的线 = f_vg_map变换(节点视口->m_画布, g连接线变换ID);


#pragma omp parallel for
	for (int32 i = 0; i < ws.m_Num; ++i) {
		assert(dynamic_cast<C_节点面板*>(ws.m_W[i]));
		C_节点面板* 节点 = (C_节点面板*)(ws.m_W[i]);

		//节点->f_setPos(节点->f_getOriginalPos() * 节点视口->m_扩展视口属性->m_比例 + 节点视口->m_扩展视口属性->m_中心);
		//f_NodePlane_更新节点内部面板(节点);
		f_NodePlane_更新布局(节点);
		f_widget_单个部件裁剪(节点, 节点视口->m_裁剪大小, 节点视口->m_GlobalLoc);
	}


}

static void f_节点缩放(C_节点视口* 节点视口) {
	if (!节点视口->m_节点树后端) return;

	f_Widget_计算偏移比例(节点视口);


	auto ws = 节点视口->mf_get更新组件(节点视口);
#pragma omp parallel for
	for (int32 i = 0; i < ws.m_Num; ++i) {
		ws.m_W[i]->m_Scale = 节点视口->m_扩展视口属性->m_比例;
		ws.m_W[i]->f_setSize(ws.m_W[i]->f_getOriginalSize() * 节点视口->m_扩展视口属性->m_比例);
	}


	f_节点平移(节点视口);
	节点视口->f_Scale();

}





void f_NodeView_更新节点(C_节点视口* view) {
	for (auto& e : view->m_焦点节点面板) {
		e->m_NodeData;
	}
}

std::vector<C_节点面板*> f_NodeView_拷贝节点(C_节点视口* view, std::vector<C_节点基类*>& 节点面板, bool 拷贝节点树) {
	f_NodeView_清除激活节点(view);

	std::vector<C_节点基类*> 新节点 = f_node_深拷贝(节点面板.data(), 节点面板.size(), 拷贝节点树);

	std::vector<C_节点面板*> 新面板;
	uint32 num = 节点面板.size();
	for (uint32 i = 0; i < num; ++i) {
		auto* 节点面板 = view->mf_以节点构建节点(新节点[i], !拷贝节点树, false);

		f_NodeView_添加节点面板到视口(view, 节点面板, true);
		//f_NodePlane_更新布局(节点面板);
		新面板.push_back(节点面板);

		新节点[i]->f_更新上层节点(true);
	}

	if (新面板.size()) {
		f_NodeView_添加激活节点(新面板, true);
	}

	return 新面板;
}

C_节点树* f_NodeView_取以前操作节点树() {
	return S_节点数据::G当前物体节点树;
}

void f_NodeView_添加激活节点(std::vector<C_节点面板*>& nodes, bool 是否清除以前) {
	if (是否清除以前) {
		for (auto& e : nodes) {
			if(!e->m_节点当前所在视口) continue;
			f_NodeView_清除激活节点(static_cast<C_节点视口*>(e->m_节点当前所在视口));
		}
	}

	for (auto& e : nodes) {
		static_cast<C_节点视口*>(e->m_节点当前所在视口)->m_焦点节点面板.insert(e);
	}

	f_节点获取焦点(nodes.data(), nodes.size());
}

void f_NodeView_清除激活节点(C_节点视口* view) {
	for (auto* 节点面板 : view->m_焦点节点面板) {
		for (auto& node : 节点面板->m_NodeData->m_包含在多个视口的UI部件) {
			static_cast<C_节点面板*>(node)->f_设置焦点(false);
		}
	}
	view->m_焦点节点面板.clear();

	f_projectFile_清除焦点更新节点();
}

void f_NodeView_设置节点视口激活节点(std::vector<C_节点基类*>& nodes) {
	for (auto& node : nodes) {
		for (auto* plane : node->m_包含在多个视口的UI部件) {
			
			C_节点面板* 节点面板 = ((C_节点面板*)plane);
			if (节点面板->m_是否已弃用 == false) {
				((C_节点视口*)(节点面板->m_节点当前所在视口))->m_焦点节点面板.insert(节点面板);

				节点面板->m_绘制属性更新 = true;
				节点面板->f_设置焦点(true);
			}
		}
	}
}

void f_NodeView_清除所有视口中的节点() {
	for (auto& 节点视口组 : g节点视口) {
		for (auto& view : 节点视口组.second) {
			view->f_清除所有节点面板();
		}
	}

	g节点视口.clear();
}












/****************************************** 视口输入事件 *******************************************/
static bool f_节点视口鼠标按下(C_Widget* self, const E_鼠标按钮& 按钮) {
	C_节点视口* 视口 = dynamic_cast<C_节点视口*>(self);

	//return false;
	if (按钮 == E_鼠标按钮::e_鼠标左键) {
		if (g剪刀线状态_状态 & DEF_剪刀线状态_准备) {
			g剪刀线状态_状态 |= DEF_剪刀线状态_开始;
		}
		
		视口->m_剪刀线绘制.f_alloc(1, S_主题颜色::uic_节点剪刀线);
		视口->m_剪刀线绘制.f_End();


		//C_节点面板::m_移动面板 = false;
		g当前移动面板操作 = nullptr;
	}
	else if (按钮 == E_鼠标按钮::e_鼠标中键) {
		g开始移动视口 = true;
	}

	return false;
}


static bool f_节点视口鼠标放开(C_Widget* self, const E_鼠标按钮& 按钮) {
	C_节点视口* 视口 = dynamic_cast<C_节点视口*>(self);
	//return false;
	if (按钮 == E_鼠标按钮::e_鼠标左键) {
		//========================= 剪断连接线 ===========================
		if (g剪刀线状态_状态 & DEF_剪刀线状态_准备) {
			g剪刀线状态_状态 = DEF_剪刀线状态_准备;
			//操作记录
			视口->m_剪刀线绘制.f_get线坐标();

			auto e = self->f_get更新部件();
			for (uint32 i = 0; i < e.m_Num; ++i) {
				C_节点面板* 节点面板 = (C_节点面板*)(e.m_W[i]);
				vec2 面板大小 = 节点面板->m_内部面板->f_getOriginalSize() * 0.4;

				//auto 断开的节点 = 节点面板->f_切断链接(视口->m_剪刀线绘制.lines.data(), 视口->m_剪刀线绘制.lines.size());
				auto 断开的节点 = 节点面板->f_切断园公切线链接(视口->m_剪刀线绘制.lines.data(), 视口->m_剪刀线绘制.lines.size(), 面板大小.x);
				for (auto& e : 断开的节点) {
					e->f_动态删除插座();
				}
				
				节点面板->m_属性面板参数构建(视口->m_属性栏, (S_结构对象指针)节点面板);
			}

			视口->m_剪刀线绘制.f_Close();
		}

		//======================= 选中节点设置 ============================


		//节点框调整 或 节点线相交链接 节点对齐s
		if (g移动选中节点) {
			f_NodeView_节点添加到框(视口, 0);
			f_NodeView_节点相交链接(视口, 0);
			f_NodeView_节点移动对齐(视口, 0);
		}
		
		
		g移动选中节点 = false;
	}
	else if (按钮 == E_鼠标按钮::e_鼠标中键) {
		if (g开始移动视口) {
			for (auto& e : 视口->m_焦点节点面板) {
				e->m_绘制属性更新 = true;
			}
			视口->m_绘制属性更新 = true;

			
		}
		g开始移动视口 = false;

	}
	else if (按钮 == E_鼠标按钮::e_鼠标右键) {
		视口->m_焦点节点面板.clear();
		
	}

	g当前移动面板操作 = nullptr;
	C_节点面板::m_移动面板 = false;

	f_projectFile_设置文件保存状态(false);
	f_NodeCtx_get默认场景()->m_光追后端->f_重置渲染帧记录();
	return false;
}




static bool f_节点视口鼠标移动(C_Widget* self, const ivec4& mouse) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	if (!节点视口->m_节点树后端) return true;

	vec2 globalLoc{ float32(mouse.x), float32(mouse.y)};
	
	if (g开始移动视口) {
		
		//================= 移动视口 ====================
		f_Widget_视口偏移(节点视口, C_Widget::g_鼠标.abs.x, C_Widget::g_鼠标.abs.y);
		
		节点视口->f_Scale();
		
		f_节点平移(节点视口);
		f_projectFile_设置文件保存状态(false);
	}
	else if (g移动选中节点) {
		vec2 鼠标位置 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, 节点视口);
		uint32 i = 0;
		if (g当前移动面板操作) {
			g当前移动面板操作->f_移动(f_NodePanel_get标准坐标(鼠标位置, 节点视口));
			
		}
		f_projectFile_设置文件保存状态(false);
	}
	else {
		if (g剪刀线状态_状态 & DEF_剪刀线状态_开始 && g剪刀线状态_状态 & DEF_剪刀线状态_准备) {
			auto 点坐标 = (f_widget_get鼠标局部位置(globalLoc, 节点视口) - 节点视口->m_扩展视口属性->m_中心) / 节点视口->m_扩展视口属性->m_比例;
			
			节点视口->m_剪刀线绘制.f_set下一个顶点(点坐标, S_主题颜色::uic_节点剪刀线);

			f_projectFile_设置文件保存状态(false);
		}
	}
	
	//std::cout << g节点视口鼠标局部位置.x << " = " << g节点视口鼠标局部位置.y << std::endl;
	return true;
}


static bool f_节点视口鼠标滚轮(C_Widget* self, const S_鼠标& 鼠标) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	//float32 s = 0;
	g节点视口鼠标局部位置 = f_widget_get鼠标局部位置(鼠标.gPos, 节点视口);
	g节点视口鼠标局部位置 = g节点视口鼠标局部位置 - 节点视口->m_扩展视口属性->m_中心;
	auto 比例 = g节点视口鼠标局部位置 / 节点视口->m_扩展视口属性->m_缩放后边界;



	vec2 缩放量 = 节点视口->m_扩展视口属性->m_缩放后边界;
	if (鼠标.lZ > 0) {
		节点视口->m_扩展视口属性->m_比例.x *= 1.3;
		节点视口->m_扩展视口属性->m_比例.y *= 1.3;
	}
	else if (鼠标.lZ < 0) {
		节点视口->m_扩展视口属性->m_比例.x *= 0.8;
		节点视口->m_扩展视口属性->m_比例.y *= 0.8;
	}
	//节点视口->m_Scale = 节点视口->m_父部件->m_扩展视口属性->m_比例;
	if(节点视口->m_扩展视口属性->m_比例.y > 8) 节点视口->m_扩展视口属性->m_比例 = {8, 8};
	else if(节点视口->m_扩展视口属性->m_比例.y < 0.25) 节点视口->m_扩展视口属性->m_比例 = {0.25f, 0.25};
	//std::cout<<"节点视口->m_比例.y = " << 节点视口->m_比例.y << std::endl;

	
	节点视口->m_扩展视口属性->m_缩放后边界 = 节点视口->f_get边界() * 节点视口->m_扩展视口属性->m_比例;
	缩放量 = 节点视口->m_扩展视口属性->m_缩放后边界 - 缩放量;


	节点视口->m_扩展视口属性->m_中心 = 节点视口->m_扩展视口属性->m_中心 - 缩放量 * 比例;
	//f_Widget_计算偏移比例(节点视口->m_父部件);
	f_节点缩放(节点视口);
	
	节点视口->f_Scale();
	f_projectFile_设置文件保存状态(false);
	return true;
}







static bool f_节点视口_按键按下(C_Widget* self, const S_键盘& ks) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	f_设置当前操作节点视口(节点视口);

	static bool 拷贝节点 = false;
	//if(ks.Keys状态[S_Input::e_KEY_左ALT]) cout << "ks == " << int32(ks.Keys状态[S_Input::e_KEY_左ALT]) << endl;
	if (f_KS(ks, S_InputKey::e_KEY_左ALT) == DEF_按键状态_按下) {
		g剪刀线状态_状态 = DEF_剪刀线状态_准备;
		S_框架::g_开启节点移动 = false;
		//C_节点面板::m_移动面板 = false;
	}
	else if (f_KS(ks, S_InputKey::e_KEY_TAB) == DEF_按键状态_放开) {
		
		//================= 同步后端数据 ==================
		f_NodeView_设置节点面板局部添加位置(f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self), false);
		f_ui_打开菜单(节点视口->m_节点菜单, C_Widget::g_鼠标.gPos);


	} else if (f_KS(ks, S_InputKey::e_KEY_N) == DEF_按键状态_放开) {
		节点视口->m_属性栏;
	} else if (f_KS(ks, S_InputKey::e_KEY_X) == DEF_按键状态_放开) {
		f_ui_打开菜单(节点视口->m_节点删除菜单, C_Widget::g_鼠标.gPos);
	}

	if (f_KS(ks, S_InputKey::e_KEY_左ALT) == DEF_按键状态_放开 || f_KS(ks, S_InputKey::e_KEY_左ALT) == DEF_按键状态_准备) {
		g剪刀线状态_状态 = DEF_剪刀线状态_结束;
		S_框架::g_开启节点移动 = true;
	}




	return false;
}














/********************************************************************************
* 
* 
*							节点面板鼠标键盘事件
*
* 
*********************************************************************************/

static bool f_NodeView_节点面板鼠标单击(C_Widget* self, const E_鼠标按钮& 按钮) {
	C_节点面板* 节点面板 = static_cast<C_节点面板*>(self->m_父部件);
	C_节点视口* 节点视口 = static_cast<C_节点视口*>(节点面板->m_节点当前所在视口);
	
	if (!S_框架::g_开启节点移动) {
		return false;
	}
	else {
		C_节点面板::m_移动面板 = false;
	}

	
	C_节点操作_移动节点::g当前移动操作 = nullptr;
	if(g移动选中节点) {
		g当前移动面板操作 = nullptr;
		return true;
	}

	if (节点视口->m_焦点节点面板.find(节点面板) == 节点视口->m_焦点节点面板.end()) {
		//节点面板->m_属性面板参数构建(节点视口->m_属性栏, (S_结构对象指针)节点面板);
		f_NodeView_添加激活节点(std::vector<C_节点面板*>{ 节点面板 }, true);
		f_projectFile_设置文件保存状态(false);
		return true;
	}

	if (节点视口->m_焦点节点面板.size() > 1) {
		f_NodeView_添加激活节点(std::vector<C_节点面板*>{ 节点面板 }, true);
		f_projectFile_设置文件保存状态(false);
	}

	return true;
}





static bool on_节点面板鼠标进入(C_Widget* self, const S_鼠标& 轮值) {
	C_节点面板* plane = (C_节点面板*)self;
	C_节点视口* view = (C_节点视口*)(self->m_父部件);
	
	switch (plane->m_NodeData->m_Type)
	{
	case E_节点类型::e_节点Type_框: {
		//view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 1, plane->m_颜色C);
		break;
	}
	default:
		if (plane->m_NodeData->m_编译成功) {
			view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 2, plane->m_颜色C);
		}
		break;
	}
	
	
	return true;
}

static bool on_节点面板鼠标退出(C_Widget* self, const S_鼠标& 轮值) {
	C_节点面板* plane = (C_节点面板*)self;
	C_节点视口* view = (C_节点视口*)(self->m_父部件);
	
	switch (plane->m_NodeData->m_Type) {
	case E_节点类型::e_节点Type_框: {
		//if (plane->f_是否有焦点()) {
		//	view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 1, S_UI主题::uic_节点面板高亮);
		//}
		//else {
		//	view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 2, plane->m_颜色A);
		//}
		break;
	}
	default:
		if (plane->f_是否有焦点()) {
			view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 2, S_UI主题::uic_节点面板高亮);
		}
		else {
			view->m_节点面板背景绘制.f_setColor(plane->m_背景GPU内存偏移 + 2, plane->m_颜色A);
		}
		break;
	}
	return true;
}







static vec2 g选框起点坐标;
static vec2 g选框终点坐标;

static E_事件是否传递 on_节点视口鼠标准备拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	//if(!S_框架::g_开启节点移动) return E_事件是否传递::e_事件传递_终止;

	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	
	vec2 mousePos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
	g选框起点坐标 = (mousePos - 节点视口->m_扩展视口属性->m_中心) / 节点视口->m_扩展视口属性->m_比例;
	//g打开选框绘制 = true;

	return E_事件是否传递::e_事件传递_继续;
}

static E_事件是否传递 on_节点视口鼠标拖动(C_Widget* self, const S_鼠标& 鼠标, S_拖放数据& data) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	if (g剪刀线状态_状态) {

	}
	else {
		vec2 mousePos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		g选框终点坐标 = (mousePos - 节点视口->m_扩展视口属性->m_中心) / 节点视口->m_扩展视口属性->m_比例;

		节点视口->m_背景绘制.f_alloc(1);
		节点视口->m_背景线绘制.f_alloc(1);
		mat3X2 mat;
		mat.row0 = g选框起点坐标;
		mat.row1 = g选框终点坐标 - mat.row0;
		mat.row2 = { 1,0 };
		S_LineProp 线属性 = { 1.0, f_PackData4X8(S_RGBA8UI{ 50,50,255,200 }), f_PackData4X8(S_RGBA8UI{ 50,50,255,200 }) };
		节点视口->m_背景绘制.f_set单色填充(0, mat, S_RGBA8UI{ 60,80,160,100 }, S_Widget预设图元::m_圆角矩形);
		节点视口->m_背景线绘制.f_set单色线(0, mat, 线属性, S_Widget预设图元::m_圆角矩形);

		data.m_Type = E_自定义拖放数据类型::e_节点视口框选;
	}
	
	return E_事件是否传递::e_事件传递_终止;
}

static bool on_节点视口鼠标结束拖动(C_Widget* self, C_Widget* 拾取的部件, S_拖放数据& data) {
	//if(data.m_Type != E_框架_拖放数据类型::e_节点视口框选) return E_事件是否传递::e_事件传递_继续;
	if (g剪刀线状态_状态) return E_事件是否传递::e_事件传递_继续;

	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	auto 鼠标局部位置 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
	

	switch (data.m_Type) {
	case E_自定义拖放数据类型::e_节点面板移动: {
		break;
	}
	case E_自定义拖放数据类型::e_节点视口框选: {
		节点视口->m_背景绘制.f_Close();
		节点视口->m_背景线绘制.f_Close();
		//g打开选框绘制 = false;
		auto e = 节点视口->f_get更新部件();
		auto left = vec_Min(g选框起点坐标, g选框终点坐标);
		auto rigth = vec_abs(g选框终点坐标 - g选框起点坐标) + left;

		std::vector<C_节点面板*> nodes;
		nodes.reserve(100);
		for (uint32 i = 0; i < e.m_Num; ++i) {
			C_节点面板* panel = (C_节点面板*)e.m_W[i];

			vec2 pos = (panel->f_getOriginalPos() + panel->m_内部面板->f_getOriginalPos());
			vec2 r = panel->m_内部面板->f_getOriginalSize() + pos;

			if (pos.x >= left.x && pos.y >= left.y) {
				if (r.x < rigth.x && r.y < rigth.y) {
					nodes.push_back((C_节点面板*)e.m_W[i]);
				}
			}
		}

		if (nodes.size()) {
			f_NodeView_添加激活节点(nodes, true);
			
		}
		break;
	}
	case E_自定义拖放数据类型::e_拖放_节点子集按钮: {
		switch (节点视口->m_节点树后端->m_树类型) {
		case E_节点树类型::e_type_渲染节点树: {
			S_插座拖放数据* 节点拖放数据 = (S_插座拖放数据*)data.m_Next;

			auto* node = 节点拖放数据->发起的节点->m_NodeData;

			//=================== 创建物体应用节点 ======================
			switch (node->m_Type) {
			case E_节点类型::e_节点Type_摄像机: 
			case E_节点类型::e_节点Type_灯光: 
			case E_节点类型::e_节点Type_自定义: 
			case E_节点类型::e_节点Type_包: 
			case E_节点类型::e_节点Type_多边形: 
			case E_节点类型::e_节点Type_物体: {
				auto* socket1 = node->f_get输出插座(1);
				if (socket1->m_Type == E_值类型::e_Type_Object) {
					C_节点面板* 新面板 = 节点视口->mf_名称构建节点面板(DEF_引用物体节点);
					//f_NodeView_设置节点面板局部添加位置(鼠标局部位置);
					S_框架::g节点创建初始位置 = 鼠标局部位置 - f_NodePlane_get中心偏移(新面板);
					f_NodeView_添加节点面板到视口(节点视口, 新面板, true);
					f_NodePlane_鼠标设置节点坐标(新面板, S_框架::g节点创建初始位置);

					新面板->m_NodeData->f_get输出插座(1)->f_setData(socket1->f_getData(0));
				}
				break;
			}
			default:
				break;
			}
			if (node->m_TypeName == DEF_取物体节点) {

			}
			else if (node->m_TypeName == DEF_引用物体节点) {

			}
			
			break;
		}
		default:
			break;
		}

		
		break;
	}
	case E_自定义拖放数据类型::e_拖放_库项加载: {
		S_库节点加载* 库节点加载 = (S_库节点加载*)data.m_Next;
		std::cout<<库节点加载->m_Path<<std::endl;
		
		FILE* f = fopen(库节点加载->m_Path.c_str(), "rb");
		if (f) {
			//std::string ICO = f_读取字符串(f);
			//std::string 说明 = f_读取字符串(f);

			std::vector<C_节点基类*> Nodes;
			f_node后端_设置节点树读取新建状态();
			f_node后端_加载库节点(*S_框架::g_3D视口环境, Nodes, f);
			//f_node_加载节点(*S_框架::g_3D视口环境, Nodes, f);
			fclose(f);

			f_NodeView_设置节点面板局部添加位置(鼠标局部位置, false);
			f_NodeView_添加节点到视口(节点视口, Nodes, true);

			for (auto& node : Nodes) {
				node->f_更新上层节点(true);
			}
		}
		
		break;
	}
	default:
		break;
	}
	
	f_projectFile_设置文件保存状态(false);
	f_NodeCtx_get默认场景()->m_光追后端->f_重置渲染帧记录();

	return true;
}







static void on_节点面板纵向变换修改(C_节点视口* view, C_节点面板* 节点面板) {
	mat3X2 mat;

	vec2 pos = 节点面板->f_getOriginalPos();
	vec2 内部面板位置 = pos + 节点面板->m_内部面板->f_getOriginalPos();
	vec2 内部面板大小 = 节点面板->m_内部面板->f_getOriginalSize();
	uint32 背景GPU内存偏移 = 节点面板->m_背景GPU内存偏移;
	E_节点类型 节点类型 = 节点面板->m_NodeData->m_Type;

	float32 缩放 = 节点面板->m_节点当前所在视口->m_扩展视口属性->m_比例.x;

	uint32 每个面板元素数量 = g每个面板元素数量;
	{
		switch (节点类型) {
		case E_节点类型::e_节点Type_框: {
			mat.row0 = 内部面板位置;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };

			S_RGBA8UI 高亮;
			if (节点面板->f_是否有焦点()) {
				高亮 = S_RGBA8UI{ 60, 100, 100, 120 };
			}
			else {
				高亮 = S_RGBA8UI{ 60, 60, 60, 60 };
			}
			view->m_节点框绘制.f_set单色填充(背景GPU内存偏移, mat, S_主题颜色::uic_节点框, 节点面板->m_图元);

			mat.row0 = 内部面板位置 + 2;
			mat.row1 = 内部面板大小 - 4;
			S_LineProp lp = { 0, f_PackData4X8(S_主题颜色::uic_节点框 + 高亮), f_PackData4X8(S_主题颜色::uic_节点框 + 高亮) };
			view->m_节点框边绘制.f_set单色线(背景GPU内存偏移, mat, lp, 节点面板->m_图元, 3);
			每个面板元素数量 = 2;
			break;
		}
		default:
			S_RGBA8UI 焦点颜色 = { 10,10,10,200 };
			if (节点面板->m_NodeData->m_冻结数据) {
				焦点颜色 = { 30,30,30,128 };
			}
			mat.row0 = 内部面板位置 + 4;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移, mat, 焦点颜色, 节点面板->m_图元);


			//焦点颜色 = 节点面板->m_焦点颜色;
			焦点颜色 = S_主题颜色::uic_节点面板焦点 + S_RGBA8UI{ 50,80,200,0 };
			if (节点面板->m_NodeData->m_冻结数据 == false) {
				焦点颜色 -= S_RGBA8UI{ 30, 10, 0, 0 };
			}
			mat.row0 = 内部面板位置 - 2;
			mat.row1 = 内部面板大小 + 4;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			if (节点面板->f_是否有焦点())
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 1, mat, 焦点颜色, 节点面板->m_图元);
			else
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 1, mat, { 0,0,0,0 }, 节点面板->m_图元);



			焦点颜色 = 节点面板->m_NodeData->m_颜色;
			if (!节点面板->m_NodeData->m_编译成功) {
				焦点颜色 = S_RGBA8UI{ 255, 10, 0, 255 };
			}
			mat.row0 = 内部面板位置;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			if (节点面板->f_是否有焦点())
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 2, mat, 焦点颜色 + S_RGBA8UI{ 60, 60, 60, 10 }, 节点面板->m_图元);
			else
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 2, mat, 焦点颜色, 节点面板->m_图元);


			mat.row0 = pos + 节点面板->m_拖拽到其他视口按钮->f_getOriginalPos();
			mat.row1 = 节点面板->m_拖拽到其他视口按钮->f_getOriginalSize();
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			if (节点面板->f_是否有焦点())
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 3, mat, 节点面板->m_拖拽到其他视口按钮->m_颜色A + S_RGBA8UI{ 0, 0, 40, 0 }, S_Widget预设图元::m_右圆角矩形);
			else
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 3, mat, 节点面板->m_拖拽到其他视口按钮->m_颜色A, S_Widget预设图元::m_右圆角矩形);


			mat.row0 = pos + 节点面板->m_独显按钮->f_getOriginalPos();
			mat.row1 = 节点面板->m_独显按钮->f_getOriginalSize();
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			if (节点面板->f_是否有焦点())
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 4, mat, 节点面板->m_独显按钮->m_颜色A + S_RGBA8UI{ 40, 20, 20, 0 }, S_Widget预设图元::m_左圆角矩形);
			else
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 4, mat, 节点面板->m_独显按钮->m_颜色A, S_Widget预设图元::m_左圆角矩形);
			break;
		}
		

	}
	


	vec2 中心局部坐标 = f_NodePlane_get中心偏移(节点面板);
	mat.row0 = pos + 中心局部坐标;
	mat.row1.x = mat.row1.y;
	mat.row2 = {};
	view->m_节点面板图标绘制.f_set(节点面板->m_ICO, 节点面板->m_实例ID, mat, { 0,0,0,0 });
	


	
	const auto& 字体 = f_surface_get字体库(*(view->m_UIctx), S_UI主题::ui_默认字体);
	uint32 文字GPU偏移 = 节点面板->m_文字GPU内存偏移;
	
	//if (节点面板->m_节点当前所在视口->m_扩展视口属性->m_比例.x < 1.5) {
	//	view->m_节点面板文本绘制.f_Close(1);
	//}

	if (节点面板->m_节点当前所在视口->m_扩展视口属性->m_比例.x > 0.5) {
		auto name = 节点面板->m_NodeData->f_getWName();
		float32 width = f_surface_get字符串宽度(字体, name, S_UI主题::ui_文字间隔);
		mat.row0 = pos;

		switch (节点类型) {
		case E_节点类型::e_节点Type_框:
			mat.row0.x += (节点面板->f_getOriginalSize().x - width) * 0.5;

			mat.row1.x = 200;
			mat.row1.y = 20;
			break;
		default: {
			mat.row0.x += 节点面板->f_getOriginalSize().x + 5;
			mat.row0.y = 内部面板位置.y ;

			mat.row1.x = 200;
			mat.row1.y = 内部面板大小.y;
			break;
		}
		}
		view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_UI主题::uic_文字颜色, 文字GPU偏移, name, E_对齐方式::e_左对齐, 缩放);
		++文字GPU偏移;
	}
	
	if(每个面板元素数量 <= 2) return;



	vec2 连接点大小 = g纵向排列插座大小;
	uint32 线GPU偏移 = 节点面板->m_链接线GPU内存偏移;
	uint32 插座GPU偏移 = 背景GPU内存偏移 + 每个面板元素数量;



	uint32 线颜色 = f_PackData4X8(S_主题颜色::uic_节点链接线);
	uint32 线高亮颜色A = f_PackData4X8(S_UI主题::uic_深色 + S_UI主题::uic_面板边);
	uint32 线高亮颜色B = f_PackData4X8(S_UI主题::uic_深色 + S_RGBA8UI{ 200, 100, 20, 255 });

	S_LineProp 线属性 = { 1.0, 线颜色, 线颜色 };

	auto& inSocket = 节点面板->f_getInSocket();
	auto& outSocket = 节点面板->f_getOutSocket();

	uint32 inNum = 节点面板->m_NodeData->f_get输入插座Num();
	uint32 outNum = 节点面板->m_NodeData->f_get输出插座Num();
	//uint32 所有插座数量 = inNum + outNum;

	for (uint32 i = 0; i < inNum; ++i) {
		vec2 插座大小 = inSocket[i]->f_getOriginalSize();

		mat.row0 = pos + inSocket[i]->f_getOriginalPos();
		auto 图元 = S_Widget预设图元::m_左圆角矩形;

		float32 圆角半径 = 2;
		if (i) {
			插座大小.x -= 3;
			vec2 大小 = 插座大小;
			//插座大小 = vec_Min(连接点大小, 插座大小);
			if (inSocket[i]->m_SocketData->f_is单链接()) {
				插座大小.x = DEF_Min(插座大小.x, g纵向排列插座大小.x);
			}
			else {
				插座大小.x = DEF_Max(插座大小.x, g纵向排列插座大小.x);
			}

			插座大小.y = DEF_Min(插座大小.y, g纵向排列插座大小.y);

			大小.x = (大小.x - 插座大小.x) * 0.5;
			大小.y -= 插座大小.y;

			mat.row0.x += 大小.x + 1.5f;
			mat.row0.y += 大小.y;
			mat.row1 = 大小;

			圆角半径 = 3;
			图元 = S_Widget预设图元::m_圆角矩形;
		}
		//mat.row1 = { C_节点面板::m_链接点直径, C_节点面板::m_链接点直径 };

		mat.row1 = 插座大小;
		mat.row2 = { 圆角半径, 0 };
		view->m_节点面板背景绘制.f_set单色填充(i + 插座GPU偏移, mat, inSocket[i]->m_颜色A, 图元);

		auto name = inSocket[i]->m_SocketData->f_getTypeName();
		float32 width = f_surface_get字符串宽度(字体, name, S_UI主题::ui_文字间隔);
		//mat.row0.x += 插座大小.x * 0.5f + (width * 0.5 / 缩放);
		//mat.row1.x = 插座大小.x * 节点面板->m_节点当前所在视口->m_扩展视口属性->m_比例.x;
		//mat.row1.y = 15;
		view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, name, E_对齐方式::e_据中对齐, 缩放);
		++文字GPU偏移;


		

		auto& linkLines = inSocket[i]->m_SocketData->f_getLinkLines();
		uint16 k = 1;
		uint16 socketNum = linkLines.size();
		for (auto link : linkLines) {
			S_Line 线坐标 = 节点面板->f_计算横向排列插座连接线坐标(link, socketNum, k, i);

			S_CurvePoint point;
			point.L_point = 线坐标.start;
			point.R_point = 线坐标.end;
			point.value = { 内部面板大小.x * 0.4f, 内部面板大小.x*0.4f };
			

			if (i) {
				//图元 = S_Widget预设图元::m_竖向圆角连接线;
				point.Select_Type = { 0, DEF_LINKLINE_TYPE_CircleTangent_V };
			}
			else {
				point.Select_Type = { 0, DEF_LINKLINE_TYPE_C };
			}

			
			if (节点面板->m_NodeData->m_是否有焦点) {
				point.ID_Color = { 线高亮颜色B, 线高亮颜色B };
			}
			else if (link->m_插座out->f_getNode()->m_是否有焦点) {
				point.ID_Color = { 线高亮颜色B, 线高亮颜色B };
			}
			else {
				point.ID_Color = { 线颜色, 线颜色 };
			}

			view->m_节点面板连接线绘制.f_set(线GPU偏移, point);
			++线GPU偏移;
			++k;
		}
	}

	插座GPU偏移 += inNum;
	for (uint32 i = 0; i < outNum; ++i) {
		vec2 插座大小 = outSocket[i]->f_getOriginalSize();
		mat.row0 = pos + outSocket[i]->f_getOriginalPos();
		auto 图元 = S_Widget预设图元::m_左圆角矩形;

		float32 圆角半径 = 3;
		if (i) {
			vec2 大小 = 插座大小;
			插座大小 = vec_Min(连接点大小, 插座大小);

			大小.x = (大小.x - 插座大小.x) * 0.5;

			mat.row0.x += 大小.x;
			圆角半径 = 2;
			图元 = S_Widget预设图元::m_圆角矩形;
		}
		mat.row1 = 插座大小;
		mat.row2 = { 圆角半径, 0 };
		view->m_节点面板背景绘制.f_set单色填充(i + 插座GPU偏移, mat, outSocket[i]->m_颜色A, 图元);


		auto name = outSocket[i]->m_SocketData->f_getTypeName();
		float32 width = f_surface_get字符串宽度(字体, name, S_UI主题::ui_文字间隔);
		//mat.row0.x += 插座大小.x * 0.5f - (width * 0.5 / 缩放);
		//mat.row1 = 插座大小;
		view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, name, E_对齐方式::e_据中对齐, 缩放);
		++文字GPU偏移;
	}


	

}

static void on_节点面板横向变换修改(C_节点视口* view, C_节点面板* 节点面板) {
	vec2 pos = 节点面板->f_getOriginalPos();
	vec2 比例 = 节点面板->m_节点当前所在视口->m_扩展视口属性->m_比例;
	vec2 内部面板位置 = pos + 节点面板->m_内部面板->f_getOriginalPos();
	vec2 内部面板大小 = 节点面板->m_内部面板->f_getOriginalSize();

	E_节点类型 节点类型 = 节点面板->m_NodeData->m_Type;
	uint32 背景GPU内存偏移 = 节点面板->m_背景GPU内存偏移;
	uint32 每个面板元素数量 = g每个面板元素数量;

	mat3X2 mat;
	{
		vec2 面板位置 = pos + 节点面板->m_内部面板->f_getOriginalPos();
		vec2 面板大小 = 节点面板->m_内部面板->f_getOriginalSize();


		switch (节点面板->m_NodeData->m_Type) {
		case E_节点类型::e_节点Type_框: {
			mat.row0 = 面板位置;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };

			S_RGBA8UI 高亮;
			if (节点面板->f_是否有焦点()) {
				高亮 = S_RGBA8UI{ 60, 100, 180, 120 };
			}
			else {
				高亮 = S_RGBA8UI{ 60, 60, 60, 60 };
			}
			view->m_节点框绘制.f_set单色填充(背景GPU内存偏移, mat, S_主题颜色::uic_节点框, 节点面板->m_图元);

			mat.row0 = 面板位置 + 2;
			mat.row1 = 内部面板大小 - 4;
			S_LineProp lp = {0, f_PackData4X8(S_主题颜色::uic_节点框 + 高亮), f_PackData4X8(S_主题颜色::uic_节点框 + 高亮) };
			view->m_节点框边绘制.f_set单色线(背景GPU内存偏移, mat, lp, 节点面板->m_图元, 3);
			每个面板元素数量 = 2;
			break;
		}
		default:

			S_RGBA8UI 焦点颜色 = { 10,10,10,200 };
			if (节点面板->m_NodeData->m_冻结数据) {
				焦点颜色 = { 30,30,30,128 };
			}
			mat.row0 = 面板位置 + 4;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移, mat, 焦点颜色, 节点面板->m_图元);


			焦点颜色 = S_主题颜色::uic_节点面板焦点 + S_RGBA8UI{ 50,80,200,0 };
			if (节点面板->m_NodeData->m_冻结数据 == false) {
				焦点颜色 -= S_RGBA8UI{ 30, 10, 0, 0 };
			}
			
			mat.row0 = 面板位置 - 2;
			mat.row1 = 内部面板大小 + 4;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			if (节点面板->f_是否有焦点())
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 1, mat, 焦点颜色, 节点面板->m_图元);
			else
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 1, mat, { 0,0,0,0 }, 节点面板->m_图元);


			焦点颜色 = 节点面板->m_NodeData->m_颜色;
			if (!节点面板->m_NodeData->m_编译成功) {
				焦点颜色 = S_RGBA8UI{ 255, 10, 0, 255 };
			}
			mat.row0 = 面板位置;
			mat.row1 = 内部面板大小;
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			if (节点面板->f_是否有焦点())
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 2, mat, 焦点颜色 + S_RGBA8UI{ 60, 60, 60, 0 }, 节点面板->m_图元);
			else
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 2, mat, 焦点颜色, 节点面板->m_图元);


			mat.row0 = pos + 节点面板->m_拖拽到其他视口按钮->f_getOriginalPos();
			mat.row1 = 节点面板->m_拖拽到其他视口按钮->f_getOriginalSize();
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			if (节点面板->f_是否有焦点())
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 3, mat, 节点面板->m_拖拽到其他视口按钮->m_颜色A + S_RGBA8UI{ 0, 0, 40, 0 }, S_Widget预设图元::m_右上圆角矩形);
			else
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 3, mat, 节点面板->m_拖拽到其他视口按钮->m_颜色A, S_Widget预设图元::m_右上圆角矩形);


			mat.row0 = pos + 节点面板->m_独显按钮->f_getOriginalPos();
			mat.row1 = 节点面板->m_独显按钮->f_getOriginalSize();
			mat.row2 = { S_UI主题::ui_面板圆角半径, 0 };
			if (节点面板->f_是否有焦点())
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 4, mat, 节点面板->m_独显按钮->m_颜色A + S_RGBA8UI{ 40, 20, 20, 0 }, S_Widget预设图元::m_左上圆角矩形);
			else
				view->m_节点面板背景绘制.f_set单色填充(背景GPU内存偏移 + 4, mat, 节点面板->m_独显按钮->m_颜色A, S_Widget预设图元::m_左上圆角矩形);
			break;
		}

	}



	mat.row0 = pos + 节点面板->f_getOriginalSize() * 0.5f;
	mat.row1.x = mat.row1.y;
	mat.row2 = {};
	view->m_节点面板图标绘制.f_set(节点面板->m_ICO, 节点面板->m_实例ID, mat, { 0,0,0,0 });




	const auto& 字体 = f_surface_get字体库(*(view->m_UIctx), S_UI主题::ui_默认字体);
	uint32 文字GPU偏移 = 节点面板->m_文字GPU内存偏移;

	if (比例.x > 0.6) {
		auto name = 节点面板->m_NodeData->f_getWName();
		float32 width = f_surface_get字符串宽度(字体, name, S_UI主题::ui_文字间隔);
		mat.row0 = pos;

		switch (节点类型) {
			case E_节点类型::e_节点Type_框:
				mat.row0.x += (节点面板->f_getOriginalSize().x - width) * 0.5;

				mat.row1.x = 内部面板大小.x;
				mat.row1.y = 20;
				break;
			default: {
				mat.row0 = pos;
				mat.row0.x += 节点面板->f_getOriginalSize().x + 5;
				mat.row0.y += (节点面板->f_getOriginalSize().y * 0.5f);// +(字体.m_字号 / 节点面板->m_节点视口->m_扩展视口属性->m_比例.x * 0.5f);
				mat.row1.x = 200 * 比例.x;
				mat.row1.y = 1;
				break;
			}
		}
		view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_UI主题::uic_文字颜色, 文字GPU偏移, name, E_对齐方式::e_左对齐);
		++文字GPU偏移;
	}
	else {
		view->m_节点面板文本绘制.f_Close();
	}
	if (每个面板元素数量 <= 2) return;


	
	
	{
		auto& inSocket = 节点面板->f_getInSocket();
		auto& outSocket = 节点面板->f_getOutSocket();

		//uint32 inNum = 节点面板->m_NodeData->f_get输入插座Num();
		//uint32 outNum = 节点面板->m_NodeData->f_get输出插座Num();
		uint32 inNum = inSocket.size();
		uint32 outNum = outSocket.size();

		S_LineProp 线属性;
		uint32 线颜色 = f_PackData4X8(S_主题颜色::uic_节点链接线);
		uint32 线高亮颜色A = f_PackData4X8(S_主题颜色::uic_节点链接线 + S_UI主题::uic_面板边);
		uint32 线高亮颜色B = f_PackData4X8(S_主题颜色::uic_节点链接线 + S_RGBA8UI{ 200, 100, 20, 255 });

		
		线属性 = { 1.0, 线颜色, 线颜色 };


		
		vec2 连接点大小 = g纵向排列插座大小;
		uint32 线GPU偏移 = 节点面板->m_链接线GPU内存偏移;
		uint32 插座GPU偏移 = 背景GPU内存偏移 + 每个面板元素数量;

		for (uint32 i = 0; i < inNum; ++i) {
			vec2 插座大小 = inSocket[i]->f_getOriginalSize();

			mat.row0 = pos + inSocket[i]->f_getOriginalPos();
			auto 图元 = S_Widget预设图元::m_上圆角矩形;

			float32 圆角半径 = 2;
			if (i) {
				vec2 大小 = 插座大小;
				if (inSocket[i]->m_SocketData->f_is单链接()) {
					插座大小.x = DEF_Min(插座大小.x, g纵向排列插座大小.x);
				}
				else {
					插座大小.x = DEF_Min(插座大小.x, g纵向排列插座大小.x);
				}
				插座大小 -= 2;
				圆角半径 = 3;

				mat.row0.x += 2;
				图元 = S_Widget预设图元::m_左圆角矩形;
			}
			mat.row1 = 插座大小;
			mat.row2 = { 圆角半径, 0 };
			view->m_节点面板背景绘制.f_set单色填充(i + 插座GPU偏移, mat, inSocket[i]->m_颜色A, 图元);

			if (i && 比例.x >= 1) {
				auto name = inSocket[i]->m_SocketData->f_getTypeName() + L":" + inSocket[i]->m_SocketData->m_identifier;
				//float32 width = f_surface_get字符串宽度(字体, name, S_UI主题::ui_文字间隔);
				mat.row0.x += (插座大小.x) + 3;
				mat.row0.y += (插座大小.y*0.5 - (字体.m_字号*0.5+2) / 比例.x);
				mat.row1.x = 内部面板大小.x * 比例.x;
				mat.row1.y = 15;
				view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, name, E_对齐方式::e_左对齐);
			}
			else {
				view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, L"", E_对齐方式::e_左对齐);
			}
			++文字GPU偏移;




			auto& linkLines = inSocket[i]->m_SocketData->f_getLinkLines();
			uint16 k = 1;
			uint16 socketNum = linkLines.size();
			for (auto link : linkLines) {
				S_Line 线坐标 = 节点面板->f_计算纵向排列插座连接线坐标(link, socketNum, k, i);

				vec2 大小 = 线坐标.end - 线坐标.start;
				mat3X2 mat;
				mat.row0 = 线坐标.start;
				mat.row1 = 大小;
				mat.row2 = { DEF_Min(fabs(大小.x), fabs(大小.y)) * 0.5f, 0 };

				S_CurvePoint point;
				point.L_point = 线坐标.start;
				point.R_point = 线坐标.end;
				point.value = { 20, 20 };
				

				if (i) {
					//图元 = S_Widget预设图元::m_横向圆角连接线;
					point.Select_Type = { 0, DEF_LINKLINE_TYPE_CircleTangent_H };
				}
				else {
					point.Select_Type = { 0, DEF_LINKLINE_TYPE_CircleTangent_V };
				}

				if (节点面板->m_NodeData->m_是否有焦点) {
					point.ID_Color = { 线高亮颜色B, 线高亮颜色B };
				}
				else if (link->m_插座out->f_getNode()->m_是否有焦点) {
					point.ID_Color = { 线高亮颜色A, 线高亮颜色B };
				}
				else {
					point.ID_Color = { 线颜色, 线颜色 };
				}
				
				
				view->m_节点面板连接线绘制.f_set(线GPU偏移, point);
				++线GPU偏移;
				++k;
			}
		}

		插座GPU偏移 += inNum;
		for (uint32 i = 0; i < outNum; ++i) {
			vec2 插座大小 = outSocket[i]->f_getOriginalSize();
			mat.row0 = pos + outSocket[i]->f_getOriginalPos();
			
			auto 图元 = S_Widget预设图元::m_下圆角矩形;
			mat.row1 = 插座大小;

			float32 圆角半径 = 3;
			if (i) {
				mat.row0.y += 2;
				圆角半径 = 2;
				mat.row1 -= 2;
				
				图元 = S_Widget预设图元::m_右圆角矩形;
			}
			
			mat.row2 = { 圆角半径, 0 };
			view->m_节点面板背景绘制.f_set单色填充(i + 插座GPU偏移, mat, outSocket[i]->m_颜色A, 图元);

			if (i && 比例.x >= 1) {
				auto name = outSocket[i]->m_SocketData->f_getTypeName() + L":" + outSocket[i]->m_SocketData->m_identifier;
				float32 width = f_surface_get字符串宽度(字体, name, S_UI主题::ui_文字间隔);
				mat.row0.x -= 内部面板大小.x;
				mat.row0.y += (插座大小.y*0.5 - (字体.m_字号*0.5+2) / 比例.x);

				mat.row1.x = 内部面板大小.x;
				mat.row1.y = 15;
				view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, name, E_对齐方式::e_右对齐, 比例.x);
				
			}
			else {
				view->m_节点面板文本绘制.f_绘制文本({ mat.row0 , mat.row1 }, S_主题颜色::uic_节点插座文字, 文字GPU偏移, L"", E_对齐方式::e_右对齐);
			}
			++文字GPU偏移;
		}
	}


}

static void f_节点视口布局(C_Widget* self, C_Widget** w, uint32 num) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	f_Widget_计算视口缩放保持偏移比例(self);

	for (uint32 i = 0; i < num; ++i) {
		C_节点面板* node = static_cast<C_节点面板*>(w[i]);

		switch (node->m_NodeData->m_Type) {
			case E_节点类型::e_节点Type_框: {
				break;
			}
			default: {
				f_NodePlane_更新布局(node);
			}
		}
	}

	for (uint32 i = 0; i < num; ++i) {
		C_节点面板* node = static_cast<C_节点面板*>(w[i]);

		switch (node->m_NodeData->m_Type) {
			case E_节点类型::e_节点Type_框: {
				if (!node->m_NodeData->m_框节点) {
					f_NodePanel_更新框(node);
				}
				break;
			}
		}
	}

}


static void f_节点视口Draw(C_Widget* self, S_2D画布* 画布) {
	C_节点视口* 视口 = dynamic_cast<C_节点视口*>(self);
	S_凸边图元 图元;
	

	图元 = {};
	图元.属性 = 视口->m_视口画布属性;
	视口->m_节点框绘制.f_Draw(画布, 图元, E_笔刷类型::e_单色填充);
	图元 = {};
	图元.属性 = 视口->m_视口画布属性;
	视口->m_节点框边绘制.f_Draw(画布, 图元, E_笔刷类型::e_单色描边, 3);



	图元 = {};
	图元.属性 = 视口->m_视口画布属性;
	视口->m_节点区域绘制.f_Draw(画布, 图元, E_笔刷类型::e_单色填充);
	图元 = {};
	图元.属性 = 视口->m_视口画布属性;
	视口->m_节点区域线绘制.f_Draw(画布, 图元, E_笔刷类型::e_单色描边);

	图元 = {};
	图元.属性 = 视口->m_视口画布属性;
	//视口->m_连接线绘制.f_Draw(画布, 图元, E_笔刷类型::e_圆公切链接线);
	视口->m_连接线绘制.f_Draw(画布, g面板绘制属性ID, E_笔刷类型::e_圆公切链接线);
	视口->m_剪刀线绘制.f_Draw(画布, g面板绘制属性ID);


	S_凸边图元 链接参数{};
	链接参数.属性 = 视口->m_视口画布属性;
	视口->m_节点面板连接线绘制.f_Draw(画布, g面板绘制属性ID, E_笔刷类型::e_圆公切链接线);
	


	链接参数 = {};
	链接参数.属性 = 视口->m_视口画布属性;
	视口->m_节点面板背景绘制.f_Draw(画布, 链接参数, E_笔刷类型::e_单色填充);




	图元 = {};
	图元.属性 = 视口->m_视口画布属性;
	视口->m_背景绘制.f_Draw(画布, 图元, E_笔刷类型::e_单色填充);
	图元 = {};
	图元.属性 = 视口->m_视口画布属性;
	视口->m_背景线绘制.f_Draw(画布, 图元, E_笔刷类型::e_单色描边);

	

	视口->m_节点面板图标绘制.f_Draw彩色(画布, g面板绘制属性ID, S_节点UI主题::ui_节点图标);
	
	auto 字体 = f_surface_get字体(*self->m_UIctx, S_UI主题::ui_默认字体);
	视口->m_节点面板文本绘制.f_Draw(画布, &字体, g面板绘制属性ID);


	图元 = {};
	图元.属性 = f_vg_gen属性(self->m_UIctx->m_Ctx);
	视口->m_节点缩略图绘制.f_Draw(画布, 图元, E_笔刷类型::e_单色填充);


	return;
}


static void on_节点视口变换修改(C_Widget* self) {
	C_节点视口* 视口 = dynamic_cast<C_节点视口*>(self);
	视口->f_Scale();
	
	auto& nodes = 视口->m_节点树后端->m_Nodes;
	auto e = 视口->f_get更新部件();
	
	uint32 节点数量 = nodes.size();
	

	#pragma omp parallel sections 
	{
		#pragma omp section 
		{
			uint32 所有词数量 = 0;
			uint32 所有链接线数量 = 0;
			uint32 所有面板元素数量 = 0;

			uint32 所有框面板元素数量 = 0;

			
			std::vector<mat2x4> 顶点;
			std::vector<S_凸包顶点区间和颜色> 凸包顶点区间;
			顶点.reserve(100);

			uint32 凸包顶点区间偏移 = 0;
			for (uint32 i = 0; i < e.m_Num; ++i) {
				C_节点面板* panel = (C_节点面板*)e.m_W[i];
				panel->m_实例ID = i;

				panel->m_文字GPU内存偏移 = 所有词数量;
				panel->m_链接线GPU内存偏移 = 所有链接线数量;


				uint32 插座数量;
				switch (panel->m_NodeData->m_Type) {
				case E_节点类型::e_节点Type_框:
					panel->m_背景GPU内存偏移 = 所有框面板元素数量;
					++所有框面板元素数量;
					插座数量 = 0;
					break;
				default:
					panel->m_背景GPU内存偏移 = 所有面板元素数量;
					//插座数量 = panel->m_NodeData->f_get输入插座Num() + panel->m_NodeData->f_get输出插座Num();
					所有面板元素数量 += g每个面板元素数量;
					插座数量 = panel->m_NodeData->f_get输入插座Num() + panel->m_NodeData->f_get输出插座Num();
					break;
				}
				
				所有面板元素数量 += 插座数量;
				//节点名称
				++插座数量;
				所有词数量 += 插座数量;
				所有链接线数量 += panel->m_NodeData->f_get输入插座链接数量();




				if(panel->m_NodeData->m_区域父节点) continue;
				switch (panel->m_NodeData->m_Type) {
					case E_节点类型::e_节点Type_判断:
					case E_节点类型::e_节点Type_循环迭代:
					case E_节点类型::e_节点Type_迭代:
					case E_节点类型::e_节点Type_循环: {
						f_收集节点凸包顶点(视口, panel, 顶点, 凸包顶点区间);
						break;
					}
				}
			}

			视口->m_节点面板背景绘制.f_alloc(所有面板元素数量);
			视口->m_节点面板连接线绘制.f_alloc(所有链接线数量);
			视口->m_节点面板文本绘制.f_alloc(所有词数量, 字符跨度);
			视口->m_节点面板图标绘制.f_alloc(e.m_Num);

			视口->m_节点框绘制.f_alloc(所有框面板元素数量);
			视口->m_节点框边绘制.f_alloc(所有框面板元素数量);


			#pragma omp parallel sections 
			{
				#pragma omp section 
				{
					//#pragma omp parallel for
					switch (视口->m_节点树后端->m_布局方向) {
					case E_方向::e_横向:
						for (int32 i = 0; i < e.m_Num; ++i) {
							on_节点面板横向变换修改(视口, (C_节点面板*)e.m_W[i]);
						}
						break;
					case E_方向::e_纵向:
						for (int32 i = 0; i < e.m_Num; ++i) {
							on_节点面板纵向变换修改(视口, (C_节点面板*)e.m_W[i]);
						}
						break;
					}
				}
				#pragma omp section 
				{
					if (顶点.size()) {
						uint32 凸包数量 = 凸包顶点区间.size();

						if (凸包数量) {
							auto* R_ptr = 凸包顶点区间.data();
							vec2* 凸包顶点 = (vec2*)(顶点.data());

							S_凸边顶点* 凸边顶点 = f_凸边_创建();

							
							S_LineProp 线属性 = { 0, f_PackData4X8({ 255, 90, 0, 120 }), f_PackData4X8({ 255, 120, 0, 120 }) };
							视口->m_节点区域绘制.f_alloc(凸包数量);
							视口->m_节点区域线绘制.f_alloc(凸包数量);

							uvec2 offset = { 0, 0 };
							for (int32 i = 凸包数量 - 1; i >= 0; --i) {
								uvec2 r = R_ptr[i].顶点区间;
								offset.y = f_graph_计算2D凸包(&(凸包顶点[r.x]), r.y, 凸边顶点);
								R_ptr[i].顶点区间 = offset;

								offset.x = offset.y;
							}
					
							//std::vector<vec2> t;
							//for (uint32 i = 0; i < 凸边顶点->count; ++i) {
							//	t.push_back(凸边顶点->ptr_userData[i]);
							//}
							//uint32 添加顶点后顶点偏移 = 视口->m_节点区域绘制.f_fill顶点(t.data(), t.size());

							uint32 添加顶点后顶点偏移 = 视口->m_节点区域绘制.f_fill顶点(凸边顶点->ptr_userData, 凸边顶点->count);
							uint32 添加线后顶点偏移 = 视口->m_节点区域线绘制.f_fill顶点(凸边顶点->ptr_userData, 凸边顶点->count);
							for (uint32 i = 0; i < 凸包数量; ++i) {
								uvec2 r = R_ptr[i].顶点区间;
								mat3X2 mat;
								mat.row0 = {};
								mat.row1 = { 1,1 };
								mat.row2 = { 1,0 };

								S_RGBA8UI rgba = R_ptr[i].区颜色;
								线属性.m_ColorA = f_PackData4X8(rgba + S_RGBA8UI{0,0,0,100});
								线属性.m_ColorB = 线属性.m_ColorA;
								视口->m_节点区域绘制.f_set单色填充(i, mat, rgba, r + 添加顶点后顶点偏移);
								视口->m_节点区域线绘制.f_set单色线(i, mat, 线属性, r + 添加线后顶点偏移, 2);
								//视口->m_节点区域绘制.f_set单色填充(i, mat, { 255, 150, 0, 30 }, uvec2{} + 添加顶点后顶点偏移 );
							}

							f_凸边_销毁(凸边顶点);
						}

					}
					else {
						视口->m_节点区域绘制.f_Close();
						视口->m_节点区域线绘制.f_Close();
					}
				}
			}

		}
		#pragma omp section 
		{
			
		}
	}



	视口->m_节点缩略图绘制.f_alloc(1 + e.m_Num);

	float32 缩放 = 视口->m_扩展视口属性->m_比例.x;
	vec2 偏移 = 视口->m_扩展视口属性->m_中心;

	mat3X2 mat;
	if (self->m_Size.x > self->m_Size.y) {
		mat.row1 = { 200, self->m_Size.y / self->m_Size.x * 200 };
	}
	else {
		mat.row1 = { self->m_Size.x / self->m_Size.y * 200, 200 };
	}
	mat.row0 = { self->m_Size.x - mat.row1.x, self->m_Size.y - mat.row1.y };
	mat.row2 = { 4,0 };
	视口->m_节点缩略图绘制.f_set单色填充(e.m_Num, mat, { 80,80,80,128 }, S_Widget预设图元::m_圆角矩形);

	float32 半径 = mat.row1.x * 0.5;
	mat.row0 += 半径;

#pragma omp parallel for
	for (int32 i = 0; i < e.m_Num; ++i) {
		mat3X2 节点变换;
		节点变换.row0 = e.m_W[i]->m_Pos / 缩放 / (mat.row1 * 100) * mat.row1;
		节点变换.row1 = e.m_W[i]->m_Size / 缩放 / (mat.row1 * 100) * mat.row1;

		//float32 透明度 = DEF_Min(半径 / vec_len((节点变换.row0 - 偏移)), 1);
		float32 透明度 = 1;
		透明度 = DEF_Min(透明度 * 255, 255);

		节点变换.row0 += mat.row0;
		节点变换.row2 = { 0,0 };
		视口->m_节点缩略图绘制.f_set单色填充(i, 节点变换, { 200,200,200, uint8(DEF_Max(透明度, 10)) }, S_Widget预设图元::m_圆角矩形);
	}
	
}







/**********************************************************************************
*								视口区
*
*
*
**********************************************************************************/

//
C_节点视口::C_节点视口(S_UI创建环境& ctx, C_节点树* tree) : C_Widget(&ctx), m_节点树后端(tree) {
	mf_DrawFun = f_节点视口Draw;
	mf_布局Fun = f_节点视口布局;
	mf_变换修改 = on_节点视口变换修改;
	mf_文件处理回调 = on_文件拖放_节点创建回调;

	mf_名称构建节点面板 = nullptr;
	mf_以节点构建节点 = nullptr;
	mf_节点重构回调 = nullptr;
	m_当前绑定节点 = nullptr;
	
	


	g面板绘制属性ID = f_vg_gen属性(m_画布);
	m_视口画布属性 = f_vg_get属性(m_画布, g面板绘制属性ID);

	mf_键盘按键敲击	= f_节点视口_按键按下;
	m_视图大小		= { 2048, 2048 };

	f_setMinSize(m_视图大小);
	mf_鼠标按键按下 = f_节点视口鼠标按下;
	mf_鼠标按键放开 = f_节点视口鼠标放开;
	mf_鼠标移动		= f_节点视口鼠标移动;
	mf_鼠标滚轮		= f_节点视口鼠标滚轮;

	mf_开始拖拽	= on_节点视口鼠标准备拖动;
	mf_拖拽		= on_节点视口鼠标拖动;
	mf_结束拖拽	= on_节点视口鼠标结束拖动;


	g_ctx = &ctx;
	m_节点菜单 = 0;
	m_节点删除菜单 = 0;

	//m_节点面板名称文本对象.区间大小 = 64;
	//m_节点插座名称文本对象.区间大小 = 64;

	//g节点视口.find(tree);
	g节点视口[tree].insert(this);



	S_键盘键位映射* 快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_深度拷贝节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左SHIFT;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_左ALT;
	快捷键->键盘状态[1] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[2] = S_InputKey::e_KEY_D;
	快捷键->键盘状态[2] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_关联拷贝节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左SHIFT;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_D;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_浅拷贝节点到剪切板;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左CTRL;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_C;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_从剪切板粘贴拷拷贝节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左CTRL;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_V;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);



	

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_深拷贝节点到剪切板;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左SHIFT;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_C;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_从剪切板粘贴深拷贝节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_左SHIFT;
	快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	快捷键->键盘按键[1] = S_InputKey::e_KEY_V;
	快捷键->键盘状态[1] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);



	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_快捷键_返回上级节点;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_退格;
	快捷键->键盘状态[0] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_节点从框中移出;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_P;
	快捷键->键盘状态[0] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);

	快捷键 = f_widget_init_键盘位映射();
	快捷键->event = f_NodeView_移动节点面板;
	快捷键->键盘按键[0] = S_InputKey::e_KEY_G;
	快捷键->键盘状态[0] = DEF_按键状态_按下;
	f_ui_注册键盘快捷键(this, 快捷键);

	S_鼠标双击键位映射* 鼠标双击键位 = f_widget_alloc_鼠标双击键位映射();
	鼠标双击键位->event = f_NodeView_打开返回上级节点面板;
	鼠标双击键位->鼠标按键[0] = E_Input_鼠标按键::e_右键;
	鼠标双击键位->鼠标状态[0] = DEF_按键状态_按下;
	鼠标双击键位->发生组件 = this;
	f_ui_注册鼠标快捷键(this, (S_键位映射*)鼠标双击键位);

	//S_拖拽键位映射* 拖拽键位映射 = f_widget_init_拖拽键位映射();
	//快捷键->event = f_NodeView_移动节点面板;
	//快捷键->键盘按键[0] = S_InputKey::e_KEY_G;
	//快捷键->键盘状态[0] = DEF_按键状态_按下 | DEF_按键状态_按下中;
	//f_ui_注册键盘快捷键(this, 快捷键);

}

C_节点视口::~C_节点视口() {
}


void C_节点视口::f_remove节点面板(std::vector<C_节点面板*>& nodes) {
	std::vector<C_Widget*> 节点面板组件组;
	for (auto& k : nodes) {
		f_node_节点树删除节点(m_节点树后端, k->m_NodeData);
		节点面板组件组.push_back(k);
	}
	f_widget_remove子部件(this, 节点面板组件组, false);
}

void C_节点视口::f_update插座绘制(C_节点面板* 节点面板) {
	return;
}

vec2 C_节点视口::f_get边界() {
	return m_视图大小;
}

void C_节点视口::f_update节点面板(C_Widget* w) {
	w->f_setPos(w->f_getOriginalPos() * m_扩展视口属性->m_比例 + m_扩展视口属性->m_中心);
	w->f_setSize(w->f_getOriginalSize() * m_扩展视口属性->m_比例);
	w->m_Scale = m_扩展视口属性->m_比例;
}

void C_节点视口::f_Scale() {
	S_2D顶点属性 属性 = { {1.0f, 1.0f} };
	属性.m_比例 = m_扩展视口属性->m_比例;
	属性.m_偏移 = m_扩展视口属性->m_中心;
	属性.m_渐变数量 = g节点面板圆角半径;
	if(m_视口画布属性) f_vg_set属性(m_视口画布属性, 属性);
}









void C_节点视口::f_绑定节点树(C_节点树* tree, C_节点面板* root_plane) {
	f_清除所有节点面板();


	m_当前绑定节点 = root_plane;
	if (m_节点树后端) {
		g节点视口[m_节点树后端].erase(this);
	}

	assert(tree);

	//======================== 重建 ========================
	m_节点树后端 = tree;
	f_设置当前操作节点视口(this);
	g节点视口[tree].insert(this);
	
	
	//auto e = mf_get更新组件(this);
	uint32 回收面板数量 = m_被删除的节点面板.size();
	uint32 节点数据数量 = tree->m_Nodes.size();
	
	auto iter = tree->m_Nodes.begin();
	for (uint32 i = 0; i < 节点数据数量; ++i, ++iter) {
		C_节点面板* plane = 0;
		if (i < 回收面板数量) {
			//plane = f_回收面板组件();
		}

		plane = mf_节点重构回调(plane, *iter);
		f_NodeView_节点回调事件初始化(plane);
		plane->f_setOriginalPos((*iter)->m_坐标);

		f_update节点面板(plane);
		f_面板组件添加到视口中(plane);

		plane->m_节点当前所在视口 = this;
		plane->m_实例ID = i;
	}
}


void C_节点视口::f_清除所有节点面板(){
	if (m_画布->m_surf.size()) {
		m_节点区域绘制.f_Close();
		m_节点区域线绘制.f_Close();
		m_节点面板背景绘制.f_Close();
		m_节点面板连接线绘制.f_Close();
		m_节点面板图标绘制.f_Close();
		m_节点面板文本绘制.f_Close();

		m_连接线绘制.f_Close();
	}

	//======================== 清理 =========================
	f_NodeView_清除激活节点(this);

	//清除已有的节点面板
	for (auto& e : m_子组件) {
		f_节点面板从节点视口移入回收站((C_节点面板*)e);
	}
	f_widget_remove所有子部件(this, false);
	m_被删除的节点面板.clear();
	m_子组件.clear();
	m_焦点节点面板.clear();

	
}

C_节点面板* C_节点视口::f_从节点后端创建节点面板(C_节点基类* node, uint32 ID) {
	C_节点面板* plane = mf_节点重构回调(nullptr, node);
	f_NodeView_节点回调事件初始化(plane);
	

	plane->m_节点当前所在视口 = this;
	//plane->m_属性面板参数构建(m_属性栏, (S_结构对象指针)plane);
	plane->m_实例ID = ID;

	plane->f_setOriginalPos(node->m_坐标);

	return plane;
}

void C_节点视口::f_节点面板添加到节点视口(C_节点面板* plane, uint32 ID) {
	
	f_NodeView_节点回调事件初始化(plane);
	f_面板组件添加到视口中(plane);
	//plane->m_NodeData->m_当前节点所在节点树 = m_节点树后端;
	//plane->m_属性面板参数构建(m_属性栏, (S_结构对象指针)plane);

	
	plane->f_setOriginalPos(plane->m_NodeData->m_坐标);
	//plane->f_绑定绘制属性(m_视口画布属性);
	plane->m_节点当前所在视口 = this;
	plane->m_实例ID = ID;


	f_NodePlane_更新节点内部面板(plane);
	f_widget_单个部件裁剪(plane, this->m_裁剪大小, this->m_GlobalLoc);
}

void C_节点视口::f_面板组件添加到视口中(C_节点面板* panel) {
	switch (panel->m_NodeData->m_Type) {
	case E_节点类型::e_节点Type_框:
		f_添加子组件(panel, 0);
		break;
	default:
		f_添加子组件(panel);
		break;
	}
}

void C_节点视口::f_节点面板从节点视口移入回收站(C_节点面板* self) {
	self->m_是否已弃用 = true;
	
	self->m_NodeData->m_包含在多个视口的UI部件.erase(self);
	
	self->m_节点当前所在视口 = nullptr;
	self->m_背景GPU内存偏移 = 0;
	self->m_文字GPU内存偏移 = 0;
	self->m_链接线GPU内存偏移 = 0;

	
	uint32 sockeNum = self->m_NodeData->f_get输入插座Num();
	for (uint32 i = 0; i < sockeNum; ++i) {
		self->m_NodeData->f_get输入插座(i)->m_UI部件 = nullptr;
		self->m_NodeData->f_get输入插座(i)->on_UI更新 = nullptr;
	}
	sockeNum = self->m_NodeData->f_get输出插座Num();
	for (uint32 i = 0; i < sockeNum; ++i) {
		self->m_NodeData->f_get输出插座(i)->m_UI部件 = nullptr;
		self->m_NodeData->f_get输出插座(i)->on_UI更新 = nullptr;
	}


	auto* tree = self->m_NodeData->f_get子集树();
	if (tree) {
		if (g节点视口.find(tree) != g节点视口.end()) {
			for (auto& t : g节点视口[tree]) {
				if (t != this) {
					//t->f_清除所有节点面板();
					//t->f_节点面板从节点视口移入回收站();
				}
			}
		}
	}

	//self->f_releaseAllSocket();
	m_被删除的节点面板.push_back(self);

	f_widget_clear焦点组件();
}

C_节点面板* C_节点视口::f_回收面板组件() {
	C_节点面板* plane = m_被删除的节点面板.back();
	plane->m_是否已弃用 = false;
	plane->m_背景GPU内存偏移 = 0;
	plane->m_文字GPU内存偏移 = 0;
	plane->m_链接线GPU内存偏移 = 0;
	m_被删除的节点面板.pop_back();
	return plane;
}

void C_节点视口::f_连接线线尾拖动(vec2 beginLoc, vec2 endLoc, S_RGBA8UI c, uint32 ID) {
	m_连接线绘制.f_alloc(ID+1);

	//vec2 大小 = endLoc - beginLoc;
	//mat3X2 mat;
	//mat.row0 = beginLoc;
	//mat.row1 = endLoc;
	//mat.row1 = 大小;
	//mat.row2 = { DEF_Min(fabs(大小.x), fabs(大小.y)) * 0.3f, 0 };

	uint32 线颜色 = f_PackData4X8(c);
	S_LineProp 线属性 = { 1.0, 线颜色, 线颜色 };
	//m_连接线绘制.f_set单色线(ID, mat, 线属性, S_Widget预设图元::m_斜线, 2.0);

	S_CurvePoint point;
	point.L_point = beginLoc;
	point.R_point = endLoc;
	point.value = {30,30};
	point.ID_Color = { 0, 线颜色 };
	point.Select_Type = {0,0};

	switch (m_节点树后端->m_布局方向) {
	case E_方向::e_横向: {
		point.Select_Type.y = DEF_LINKLINE_TYPE_CircleTangent_H;
		break;
	}
	case E_方向::e_纵向: {
		point.Select_Type.y = DEF_LINKLINE_TYPE_CircleTangent_V;
		break;
	}
	}

	m_连接线绘制.f_set(ID, point);
	return;
}

void C_节点视口::f_绘制对齐线(vec2 beginLoc, vec2 endLoc, S_RGBA8UI c, uint32 ID) {
	m_连接线绘制.f_alloc(ID + 1);

	//vec2 大小 = endLoc - beginLoc;
	//mat3X2 mat;
	//mat.row0 = beginLoc;
	//mat.row1 = 大小 + 1;
	//mat.row1 = endLoc;
	//mat.row2 = { 1, 0 };

	uint32 线颜色 = f_PackData4X8(c);
	S_LineProp 线属性 = { 1.0, 线颜色, 线颜色 };


	S_CurvePoint point;
	point.L_point = vec_Min(beginLoc, endLoc);
	point.R_point = vec_Max(endLoc, beginLoc);
	point.value = { 10,10 };
	point.ID_Color = { 0, 线颜色 };
	point.Select_Type = { 0,0 };

	
	switch (m_节点树后端->m_布局方向) {
	case E_方向::e_横向: {
		point.Select_Type.y = DEF_LINKLINE_TYPE_CircleTangent_H;
		break;
	}
	case E_方向::e_纵向: {
		point.Select_Type.y = DEF_LINKLINE_TYPE_CircleTangent_V;
		break;
	}
	}

	m_连接线绘制.f_set(ID, point);
	
	return;
}

void C_节点视口::f_连接线线尾拖动结束() {
	m_连接线绘制.f_Close();
}


E_事件是否传递 f_NodeView_浅拷贝节点到剪切板(C_Widget* self, uint8 状态) {
	C_节点视口* view = dynamic_cast<C_节点视口*>(self);
	if (view) {
		S_关联拷贝节点剪切板* 剪切板 = new S_关联拷贝节点剪切板(view->m_焦点节点面板);
		剪切板->f_复制();
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_从剪切板粘贴拷拷贝节点(C_Widget* self, uint8 状态) {
	C_节点视口* view = dynamic_cast<C_节点视口*>(self);
	if (view) {
		f_设置当前操作节点视口(view);

		auto pos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		f_NodeView_设置节点面板局部添加位置(pos, false);

		S_关联拷贝节点剪切板* 剪切板 = dynamic_cast<S_关联拷贝节点剪切板*>(S_剪切板::f_get());
		if (剪切板) {
			剪切板->f_粘贴();
		}
	}
	return E_事件是否传递::e_事件传递_终止;
}



E_事件是否传递 f_NodeView_深拷贝节点到剪切板(C_Widget* self, uint8 状态) {
	C_节点视口* view = dynamic_cast<C_节点视口*>(self);
	if (view) {
		S_深拷贝节点剪切板* 剪切板 = new S_深拷贝节点剪切板(view->m_焦点节点面板);
		剪切板->f_复制();
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_从剪切板粘贴深拷贝节点(C_Widget* self, uint8 状态) {
	C_节点视口* view = dynamic_cast<C_节点视口*>(self);
	if (view) {
		f_设置当前操作节点视口(view);

		auto pos = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, self);
		f_NodeView_设置节点面板局部添加位置(pos, false);

		S_深拷贝节点剪切板* 剪切板 = dynamic_cast<S_深拷贝节点剪切板*>(S_剪切板::f_get());
		if (剪切板) {
			剪切板->f_粘贴();
		}
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_关联拷贝节点(C_Widget* self, uint8 状态) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);
	
	std::vector<C_节点基类*> 要拷贝的节点;
	要拷贝的节点.reserve(g当前操作节点视口->m_焦点节点面板.size());
	for (auto& e : g当前操作节点视口->m_焦点节点面板) {
		要拷贝的节点.push_back(e->m_NodeData);
	}

	auto new_panel = f_NodeView_拷贝节点(节点视口, 要拷贝的节点, false);
	uint32 num = 要拷贝的节点.size();

	std::vector<C_节点基类*> 新节点(num);
	for (uint32 i = 0; i < num; ++i) {
		for (auto& nodePanel : 要拷贝的节点[i]->m_包含在多个视口的UI部件) {
			new_panel[i]->m_同子集节点树关联UI部件 = ((C_节点面板*)nodePanel)->m_同子集节点树关联UI部件;
		}
		if (!new_panel[i]->m_同子集节点树关联UI部件) {
			new_panel[i]->m_同子集节点树关联UI部件 = new std::set<C_Widget*>();
		}
		(*new_panel[i]->m_同子集节点树关联UI部件).insert(new_panel[i]);

		新节点[i] = new_panel[i]->m_NodeData;

		//f_widget_单个部件裁剪(new_panel[i], 节点视口->m_裁剪大小, 节点视口->m_GlobalLoc);
	}


	auto 初始坐标 = (f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, 节点视口));
	初始坐标 = f_NodePanel_get标准坐标(初始坐标, 节点视口);
	g当前移动面板操作 = new C_节点操作_移动节点(新节点, 初始坐标);

	f_节点缩放(节点视口);
	g移动选中节点 = true;
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_深度拷贝节点(C_Widget* self, uint8 状态) {
	C_节点视口* 节点视口 = dynamic_cast<C_节点视口*>(self);

	std::vector<C_节点基类*> 要拷贝的节点;
	要拷贝的节点.reserve(g当前操作节点视口->m_焦点节点面板.size());
	for (auto& e : g当前操作节点视口->m_焦点节点面板) {
		要拷贝的节点.push_back(e->m_NodeData);
	}

	f_NodeView_拷贝节点(节点视口, 要拷贝的节点, true);


	std::vector<C_节点基类*> nodes;
	for (auto& panel : 节点视口->m_焦点节点面板) {
		nodes.push_back(panel->m_NodeData);
	}

	auto 初始坐标 = (f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, 节点视口));
	初始坐标 = f_NodePanel_get标准坐标(初始坐标, 节点视口);
	g当前移动面板操作 = new C_节点操作_移动节点(nodes, 初始坐标);


	g移动选中节点 = true;
	return E_事件是否传递::e_事件传递_终止;
}

static void f_ui_返回上级菜单项构建(std::set<C_节点基类*>& trees, uint32 深度) {
	uint32 同级ID = 0;
	for (auto& e : trees) {
		C_节点树* t = e->f_get所在节点树();
		if (g返回菜单以记录节点树.find(t) == g返回菜单以记录节点树.end()) {
			S_列表填充数据 item{};

			item.m_item.push_back(t->m_IDName);
			if (e->f_get子集树()) {
				item.m_item.push_back(e->f_get子集树()->m_IDName);
			}
			item.m_item.push_back(e->f_getWName());
			item.m_Data = t;
			
			f_ui_添加扩展弹出菜单项(C_节点视口::g节点层级切换面板, item, { 深度, 同级ID });
			g返回菜单以记录节点树.insert(t);

			f_ui_返回上级菜单项构建(t->m_节点树绑定的父节点, 深度 + 1);
		}
	}
}

static bool f_用菜单_切换节点视口绑定的节点树(C_Widget* view, const S_列表填充数据& item) {
	C_节点树* t = (C_节点树*)(item.m_Data);
	g当前操作节点视口->f_绑定节点树(t);
	f_NodeView_绑定节点视口弹出菜单(t->m_树类型, g当前操作节点视口);

	switch (t->m_树类型) {
	case E_节点树类型::e_type_物体节点树:
	case E_节点树类型::e_type_包节点树:
	case E_节点树类型::e_type_材质节点树:
	case E_节点树类型::e_type_着色节点树:
	case E_节点树类型::e_type_渲染节点树:
	case E_节点树类型::e_type_函数节点树:
		f_global_set全局物体显示();
		//f_global_set独立物体显示(t);
		break;
	default:
		f_global_set独立物体显示(t);
		break;
	}

	return true;
}

E_事件是否传递 f_NodeView_快捷键_返回上级节点(C_Widget* self, uint8 状态) {
	C_节点视口* view = static_cast<C_节点视口*>(self);
	f_设置当前操作节点视口(view);

	if (view->m_节点树后端->m_节点树绑定的父节点.size() > 2) {
		f_NodeView_打开返回上级节点面板(self, 状态);
	}
	else if (view->m_节点树后端->m_节点树绑定的父节点.size() >= 1) {
		auto 返回的节点 = *view->m_节点树后端->m_节点树绑定的父节点.begin();

		C_节点树* t = 返回的节点->f_get所在节点树();
		g当前操作节点视口->f_绑定节点树(t);
		f_NodeView_绑定节点视口弹出菜单(t->m_树类型, g当前操作节点视口);

		switch (t->m_树类型) {
		case E_节点树类型::e_type_几何节点树:
		case E_节点树类型::e_type_物体节点树:
		case E_节点树类型::e_type_包节点树:
		case E_节点树类型::e_type_材质节点树:
		case E_节点树类型::e_type_着色节点树:
		case E_节点树类型::e_type_渲染节点树:
		case E_节点树类型::e_type_函数节点树:
			f_global_set全局物体显示();
			//f_global_set独立物体显示(t);
			break;
		default:
			f_global_set独立物体显示(t);
			break;
		}
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_打开返回上级节点面板(C_Widget* self, uint8 状态) {
	C_节点视口* view = static_cast<C_节点视口*>(self);

	f_ui_绑定扩展弹出菜单项回调绑定(C_节点视口::g节点层级切换面板, f_用菜单_切换节点视口绑定的节点树);
	f_ui_删除扩展弹出菜单项(C_节点视口::g节点层级切换面板, { -1,-1 });

	g返回菜单以记录节点树.clear();
	f_ui_返回上级菜单项构建(view->m_节点树后端->m_节点树绑定的父节点, 0);

	vec2 pos = C_Widget::g_鼠标.gPos;
	pos.x -= 20;
	pos.x -= 40;
	C_节点视口::g节点层级切换面板->f_setPos(pos);

	f_设置当前操作节点视口(view);
	f_widget_顶层窗口_pop(0);
	f_widget_弹出顶层窗口(C_节点视口::g节点层级切换面板);
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_移动节点面板(C_Widget* self, uint8 状态) {
	C_节点视口* 节点视口 = static_cast<C_节点视口*>(self);

	std::vector<C_节点基类*> nodes;
	for (auto& panel : 节点视口->m_焦点节点面板) {
		nodes.push_back(panel->m_NodeData);
	}


	auto 初始坐标 = (f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, 节点视口));
	初始坐标 = f_NodePanel_get标准坐标(初始坐标, 节点视口);
	g当前移动面板操作 = new C_节点操作_移动节点(nodes, 初始坐标);


	g移动选中节点 = true;
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_节点添加到框(C_Widget* self, uint8 状态) {
	C_节点视口* 节点视口 = static_cast<C_节点视口*>(self);

	auto se = self->f_get更新部件();

	for (auto& 要添加的面板 : 节点视口->m_焦点节点面板) {
		if (要添加的面板->m_NodeData->m_框节点) continue;

		S_Bounding2D bound;
		f_NodePanel_get边界框(要添加的面板, bound);

		

		std::map<int32, C_节点基类*> 背心框节点;
		
		for (uint32 i = 0; i < se.m_Num; ++i) {
			C_节点面板* 框面板 = (C_节点面板*)se.m_W[i];

			if (要添加的面板 == 框面板) continue;
			

			
			switch (框面板->m_NodeData->m_Type){
			case E_节点类型::e_节点Type_框 : {
				if (f_NodePanel_是否已在框内(要添加的面板->m_NodeData, 框面板->m_NodeData)) continue;

				S_Bounding2D 框节点面板包围盒;
				f_NodePanel_get边界框(框面板, 框节点面板包围盒);

				if (f_geom_包围盒碰撞(bound, 框节点面板包围盒)) {
					vec2 size = (框节点面板包围盒.max - 框节点面板包围盒.min);
					背心框节点[int32(size.x * size.y)] = 框面板->m_NodeData;
				}
				break;
			}
			default:
				break;
			}
		}


		if (背心框节点.size()) {
			C_节点基类* 框节点 = (*背心框节点.begin()).second;

			框节点->m_框包含节点.emplace_back(要添加的面板->m_NodeData);
			要添加的面板->m_NodeData->m_框节点 = 框节点;

			for (auto& e : 框节点->m_包含在多个视口的UI部件) {
				uint32 源位置 = f_widget_取组件位置索引(节点视口, 要添加的面板);
				uint32 目标位置 = f_widget_取组件位置索引(节点视口, e);

				f_widget_移动层(节点视口, 源位置, 目标位置);
			}
			
		}
		
		
	}
	return E_事件是否传递::e_事件传递_终止;
}

E_事件是否传递 f_NodeView_节点相交链接(C_Widget* self, uint8 状态) {
	return E_事件是否传递();
}

E_事件是否传递 f_NodeView_节点移动对齐(C_Widget* self, uint8 状态) {
	return E_事件是否传递();
}

E_事件是否传递 f_NodeView_节点从框中移出(C_Widget* self, uint8 状态) {
	C_节点视口* 节点视口 = static_cast<C_节点视口*>(self);

	for (auto& 面板 : 节点视口->m_焦点节点面板) {
		if (面板->m_NodeData->m_框节点) {
			uint32 id = 0;
			for (auto& e : 面板->m_NodeData->m_框节点->m_框包含节点) {
				if (e == 面板->m_NodeData) {
					面板->m_NodeData->m_框节点->m_框包含节点.erase(面板->m_NodeData->m_框节点->m_框包含节点.begin() + id);
					break;
				}
				++id;
			}
			面板->m_NodeData->m_框节点 = nullptr;
		}
	}
	return E_事件是否传递::e_事件传递_终止;
}







//====================================== 菜单构建和事件函数 ============================================
void on_NodeView_删除节点菜单项(C_Widget* self, const std::wstring name, uint16 id) {
	if (name == L"删除") {
		std::vector<C_节点面板*> Nodes;
		Nodes.assign(g当前操作节点视口->m_焦点节点面板.begin(), g当前操作节点视口->m_焦点节点面板.end());

		g当前删除面板操作 = new C_节点操作_删除节点(g当前操作节点视口, Nodes);
		g当前操作节点视口->m_节点树后端->m_节点结构改变 = true;
		//g当前操作节点视口->f_remove节点面板();
		//f_节点清除焦点();
	}

}

void on_NodeView_设置当前操作视口(C_节点树* t) {
	auto& views = g节点视口[t];

	if (views.size()) {
		f_设置当前操作节点视口(*g节点视口[t].begin());
	}
	else {
		
	}
	
}

void on_NodeView_设置当前操作节点树(C_节点树* t) {

}

C_节点树* f_NodeView_取当前要操作的节点树() {
	C_节点树* 当前操作节点树 = g当前操作节点视口->m_节点树后端;
	if (!当前操作节点树) return nullptr;

	if (g当前节点添加到树_树类型标示 == 0xffffffff) {
		
	}
	else {
		switch (g当前节点添加到树_树类型标示) {
		case E_节点树类型::e_type_物体节点树: {
			当前操作节点树 = S_节点数据::G当前物体节点树;
			break;
		}
		case E_节点树类型::e_type_材质节点树: {
			当前操作节点树 = S_节点数据::G当前材质节点树;
			break;
		}
		case E_节点树类型::e_type_着色节点树: {
			当前操作节点树 = S_节点数据::G当前材质节点树;
			break;
		}
		case E_节点树类型::e_type_渲染节点树: {
			当前操作节点树 = S_节点数据::G当前渲染节点树;
			break;
		}
		case E_节点树类型::e_type_包节点树: {
			当前操作节点树 = S_节点数据::G当前包节点树;
			break;
		}
		}
	}

	return 当前操作节点树;
}

void f_NodeView_设置当前节点添加_树类型限制(uint32 限制类型位标示) {
	g当前节点添加到树_树类型标示 = 限制类型位标示;
}







/************************************
	*        节点回调事件初始化
	*************************************/
void f_NodeView_节点回调事件初始化(C_节点面板* 节点面板) {
	节点面板->m_内部面板->mf_鼠标按键点击 = f_NodeView_节点面板鼠标单击;

	节点面板->mf_鼠标进入 = on_节点面板鼠标进入;
	节点面板->mf_鼠标离开 = on_节点面板鼠标退出;
}


void f_节点View_设置操作节点(C_节点基类* node, C_节点视口* view) {
}

vec2 f_NodeView_get变换后坐标(const vec2& 变换前坐标, S_View属性* view) {
	vec2 pos = (变换前坐标 * view->m_比例) + view->m_中心;
	return pos;
}

vec2 f_NodeView_get变换前坐标(const vec2& 变换后坐标, S_View属性* view) {
	return (变换后坐标 - view->m_中心) / view->m_比例;
}


void f_NodeView_更新前选择节点() {
}




void on_节点树列表_编辑名称(S_Props& prop) {
	auto& p = f_prop_MapPrt(prop);

	p.m_PtrItem;
}

void on_节点树列表_重构节点视口内节点(S_Props& prop) {
	auto& p = f_prop_MapPrt(prop);
}


bool f_NodeView_绑定节点视口弹出菜单(E_节点树类型 节点树类型, C_节点视口* view) {

	switch (节点树类型) {
	case E_节点树类型::e_type_几何节点树:
		view->m_节点菜单 = C_节点视口::g多边形节点创建菜单;
		break;
	case E_节点树类型::e_type_物体节点树: {
		view->m_节点菜单 = C_节点视口::g物体节点创建菜单;
		break;
	}
	case E_节点树类型::e_type_材质节点树: {
		view->m_节点菜单 = C_节点视口::g材质节点创建菜单;
		break;
	}
	case E_节点树类型::e_type_着色节点树: {
		view->m_节点菜单 = C_节点视口::g着色器节点创建菜单;
		break;
	}
	case E_节点树类型::e_type_函数节点树:
		view->m_节点菜单 = C_节点视口::g函数节点创建菜单;
		break;
	case E_节点树类型::e_type_渲染节点树:
		view->m_节点菜单 = C_节点视口::g渲染节点创建菜单;
		break;
	default:
		return false;
	}

	return true;
}



//void f_NodeView_添加节点面板到视口(std::vector<C_Widget*> w, bool 是否清除以前激活节点) {
//}

std::vector<C_节点面板*> f_NodeView_从节点添加节点面板到视口(C_节点基类* node, bool 是否清除以前激活节点) {
	if (!g当前操作节点视口->m_节点树后端) return {};

	std::vector<C_节点面板*> 节点面板组;
	for (auto& view : g节点视口[node->f_get所在节点树()]) {
		C_节点面板* 节点面板 = view->f_从节点后端创建节点面板(node, 0);


		节点面板->m_节点当前所在视口 = view;
		节点面板->m_父部件 = view;


		/************************************
		*        节点后端数据
		*************************************/
		f_node_节点树添加节点(view->m_节点树后端, 节点面板->m_NodeData);
		f_NodeView_节点回调事件初始化(节点面板);
		//节点面板->m_属性面板参数构建(g当前操作节点视口->m_属性栏, (S_结构对象指针)节点面板);


		uint32 id = view->m_子组件.size();
		节点面板->m_实例ID = id;


		
		view->f_update节点面板(节点面板);
		f_NodePlane_鼠标设置节点坐标(节点面板, S_框架::g节点创建初始位置);

		f_widget_单个部件裁剪(节点面板, view->m_裁剪大小, view->m_GlobalLoc);

		节点面板->m_NodeData->f_update();

		f_NodeView_添加激活节点(std::vector<C_节点面板*>{ 节点面板 }, 是否清除以前激活节点);

		节点面板组.push_back(节点面板);
	}

	f_projectFile_设置文件保存状态(false);
	return 节点面板组;
}

//void f_节点构建_拷贝节点(C_节点视口* 节点视口) {
//}


void f_NodeView_设置节点面板局部添加位置(const vec2& 局部位置, bool 是否中心) {
	if (g当前操作节点视口) {
		if (是否中心) {
			S_框架::g节点创建初始位置 = g当前操作节点视口->m_Size * 0.5 + 局部位置;
		}
		else {
			S_框架::g节点创建初始位置 = 局部位置;
		}
	}
}

void f_NodeView_添加节点面板到视口(C_节点视口* 节点视口, C_节点面板* 节点面板, bool 是否清除以前激活节点) {
	f_NodeView_节点回调事件初始化(节点面板);

	//节点面板->m_NodeData->m_坐标 = S_框架::g节点创建初始位置 / 节点视口->m_扩展视口属性->m_比例;
	节点面板->m_实例ID = 节点视口->m_子组件.size();

	f_node_节点树添加节点(节点视口->m_节点树后端, 节点面板->m_NodeData);
	
	
	C_节点基类* nodeData = 节点面板->m_NodeData;
	for (auto& view : g节点视口[节点面板->m_NodeData->f_get所在节点树()]) {
		if (!nodeData) {
			节点面板 = view->f_从节点后端创建节点面板(节点面板->m_NodeData, 0);
		}
		节点面板->m_节点当前所在视口 = view;

		view->f_节点面板添加到节点视口(节点面板, 0);
		nodeData = nullptr;
	}
	

	f_projectFile_设置文件保存状态(false);
	f_widget_单个部件裁剪(节点面板, 节点面板->m_节点当前所在视口->m_裁剪大小, 节点面板->m_节点当前所在视口->m_GlobalLoc);
}

void f_NodeView_其他工作区添加面板到视口(C_Widget* w, bool 是否清除以前激活节点) {
	C_节点面板* 节点面板 = dynamic_cast<C_节点面板*>(w);
	C_节点树* 当前操作节点树 = g当前操作节点视口->m_节点树后端;

	if (!当前操作节点树) return;
	

	if (g当前节点添加到树_树类型标示 == 0xffffffff) {
		f_node_节点树添加节点(当前操作节点树, 节点面板->m_NodeData);
		节点面板->m_实例ID = g当前操作节点视口->m_子组件.size();
		g当前操作节点视口->f_update节点面板(节点面板);

	}
	else {
		switch (g当前节点添加到树_树类型标示) {
		case E_节点树类型::e_type_物体节点树 : {
			当前操作节点树 = S_节点数据::G当前物体节点树;
			break;
		}
		case E_节点树类型::e_type_材质节点树: {
			当前操作节点树 = S_节点数据::G当前材质节点树;
			break;
		}
		case E_节点树类型::e_type_着色节点树: {
			当前操作节点树 = S_节点数据::G当前材质节点树;
			break;
		}
		case E_节点树类型::e_type_渲染节点树: {
			当前操作节点树 = S_节点数据::G当前渲染节点树;
			break;
		}
		case E_节点树类型::e_type_包节点树: {
			当前操作节点树 = S_节点数据::G当前包节点树;
			break;
		}
		}
		g当前节点添加到树_树类型标示 = 0xffffffff;

		if (!当前操作节点树) return;
		f_node_节点树添加节点(当前操作节点树, 节点面板->m_NodeData);

		if (g节点视口.find(当前操作节点树) == g节点视口.end() || g节点视口[当前操作节点树].size() == 0) {
			节点面板->m_是否已弃用 = true;
			return;
		}
	}
	//f_NodeView_节点回调事件初始化(节点面板);
	

	C_节点基类* nodeData = 节点面板->m_NodeData;
	for (auto& view : g节点视口[当前操作节点树]) {
		if (!nodeData) {
			节点面板 = view->f_从节点后端创建节点面板(节点面板->m_NodeData, 0);
		}

		//节点面板->f_绑定绘制属性(view->m_视口画布属性);
		节点面板->m_节点当前所在视口 = view;
		
		view->f_节点面板添加到节点视口(节点面板, 0);
		f_节点缩放(view);
		nodeData = nullptr;
	}

	f_NodePlane_鼠标设置节点坐标(节点面板, S_框架::g节点创建初始位置);
	f_NodeView_添加激活节点(std::vector<C_节点面板*>{ 节点面板 }, 是否清除以前激活节点);
	f_widget_单个部件裁剪(节点面板, 节点面板->m_节点当前所在视口->m_裁剪大小, 节点面板->m_节点当前所在视口->m_GlobalLoc);

	f_projectFile_设置文件保存状态(false);
	g当前节点添加到树_树类型标示 = 0xffffffff;
}

void f_NodeView_添加节点面板到视口(std::vector<C_节点面板*>& ws, bool 是否清除以前激活节点) {
	if (是否清除以前激活节点) {
		f_NodeView_清除激活节点(g当前操作节点视口);
	}
	
	for (auto& 节点面板 : ws) {
		f_NodeView_添加节点面板到视口(g当前操作节点视口, 节点面板, false);
		f_NodePlane_鼠标设置节点坐标(节点面板, S_框架::g节点创建初始位置);
		f_widget_单个部件裁剪(节点面板, 节点面板->m_节点当前所在视口->m_裁剪大小, 节点面板->m_节点当前所在视口->m_GlobalLoc);
	}

	f_NodeView_添加激活节点(ws, true);
	f_projectFile_设置文件保存状态(false);
}

void f_NodeView_添加节点到视口(C_节点视口* 节点视口, std::vector<C_节点基类*>& ws, bool 是否清除以前激活节点) {
	g当前操作节点视口 = 节点视口;
	C_节点树* 当前操作节点树 = f_NodeView_取当前要操作的节点树();

	for (auto& e : ws) {
		f_node_节点树添加节点(当前操作节点树, e);

		for (auto& view : g节点视口[当前操作节点树]) {
			C_节点面板* 节点面板 = 节点视口->f_从节点后端创建节点面板(e, 0);

			//节点面板->f_绑定绘制属性(view->m_视口画布属性);
			节点面板->m_节点当前所在视口 = view;
			view->f_节点面板添加到节点视口(节点面板, 0);
			
			f_NodePlane_鼠标设置节点坐标(节点面板, S_框架::g节点创建初始位置 - f_NodePlane_get中心偏移(节点面板));
			f_widget_单个部件裁剪(节点面板, 节点面板->m_节点当前所在视口->m_裁剪大小, 节点面板->m_节点当前所在视口->m_GlobalLoc);
		}
	}
	f_projectFile_设置文件保存状态(false);
}







//============================================================ 撤销重做操作函数实现 ==========================================================================================
void C_节点操作_添加节点::f_撤销() {

}

void C_节点操作_添加节点::f_重做() {

}




C_节点操作_删除节点::C_节点操作_删除节点(C_节点视口* View, const std::vector<C_节点面板*>& Nodes) : m_View(nullptr) {
	for (auto& e : Nodes) {
		if(e->m_NodeData->m_冻结数据 || e->m_NodeData->m_越过更新) return;
	}
	m_节点面板 = Nodes;
	m_View = View;
	f_重做();
}

C_节点操作_删除节点::~C_节点操作_删除节点() {
	for (auto& e : m_节点面板) {

	}
}

void C_节点操作_删除节点::f_撤销() {
	if(!m_View) return;

	f_设置当前操作节点视口(m_View);

	for (auto& e : m_节点面板) {
		S_框架::g节点创建初始位置 = e->f_getPos();

		e->m_NodeData->f_回收();
		m_View->mf_节点重构回调(e, e->m_NodeData);

		f_NodeView_其他工作区添加面板到视口(e, false);
		e->m_NodeData->m_包含在多个视口的UI部件.insert(e);

		for (auto& link : m_断开的连接线) {
			link->f_Link(link->m_插座in, link->m_插座out);
		}
	}

	f_NodeView_添加激活节点(m_节点面板, true);

	m_View->m_绘制属性更新 = true;
}

void C_节点操作_删除节点::f_重做() {
	if(!m_View) return;

	f_设置当前操作节点视口(m_View);

	for (auto& e : m_节点面板) {
		e->m_NodeData->f_入回收();
		e->m_NodeData->m_区域内节点.clear();

		for (auto& socket : e->f_getInSocket()) {
			for (auto& link : socket->m_SocketData->f_getLinkLines()) {
				link->f_断开输出链接();
				m_断开的连接线.push_back(link);
			}
		}

		for (auto& socket : e->f_getOutSocket()) {
			for (auto& link : socket->m_SocketData->f_getLinkLines()) {
				link->f_断开输出链接();
				m_断开的连接线.push_back(link);
			}
		}


		if (e->m_NodeData->m_框节点) {
			for (auto it = e->m_NodeData->m_框节点->m_框包含节点.begin(); it != e->m_NodeData->m_框节点->m_框包含节点.end(); ++it) {
				if ((*it) == e->m_NodeData) {
					e->m_NodeData->m_框节点->m_框包含节点.erase(it);
					break;
				}
			}
		}


		f_node_节点树删除节点(static_cast<C_节点视口*>(e->m_节点当前所在视口)->m_节点树后端, e->m_NodeData);
		auto uis = e->m_NodeData->m_包含在多个视口的UI部件;
		for (auto& plane : uis) {
			auto nodePlane = static_cast<C_节点面板*>(plane);
			//if (nodePlane->m_节点当前所在视口) {
			f_NodeView_清除激活节点(static_cast<C_节点视口*>(nodePlane->m_节点当前所在视口));
			std::vector<C_Widget*> 节点面板组 = { nodePlane };
			f_widget_remove子部件(static_cast<C_节点视口*>(nodePlane->m_节点当前所在视口), 节点面板组, false);
			//}
			static_cast<C_节点视口*>(nodePlane->m_节点当前所在视口)->f_节点面板从节点视口移入回收站(nodePlane);
		}

	}

	m_View->m_绘制属性更新 = true;
}






void C_节点操作_选择节点::f_撤销() {

}

void C_节点操作_选择节点::f_重做() {

}

void C_节点操作_选择节点::f_记录() {

}








S_关联拷贝节点剪切板::S_关联拷贝节点剪切板(std::set<C_节点面板*>& nodes) {
	m_要拷贝的节点面板.reserve(nodes.size());

	for (auto& e : nodes) {
		m_要拷贝的节点面板.push_back(e->m_NodeData);
	}
}

void S_关联拷贝节点剪切板::f_复制() {
	uint32 num = m_要拷贝的节点面板.size();
	m_节点中心坐标偏移.resize(num);
	vec2* 中心偏移 = m_节点中心坐标偏移.data();

	vec2 中心坐标 = {};

	for (uint32 i = 0; i < num; ++i) {
		中心坐标 += m_要拷贝的节点面板[i]->m_坐标;
	}
	中心坐标 /= num;

	for (uint32 i = 0; i < num; ++i) {
		中心偏移[i] = 中心坐标 - m_要拷贝的节点面板[i]->m_坐标;
	}
}

void S_关联拷贝节点剪切板::f_粘贴() {
	std::vector<C_节点面板*> 新节点面板 = f_NodeView_拷贝节点(g当前操作节点视口, m_要拷贝的节点面板, false);

	auto 鼠标坐标 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, g当前操作节点视口);
	鼠标坐标 = f_NodeView_get变换前坐标(鼠标坐标, g当前操作节点视口->m_扩展视口属性);

	vec2* 中心偏移 = m_节点中心坐标偏移.data();

	uint32 i = 0;
	for (auto& e : 新节点面板) {
		e->m_NodeData->m_坐标 = 鼠标坐标 - 中心偏移[i];
		e->m_NodeData->m_坐标 -= f_NodePlane_get中心偏移(e);
		f_NodePlane_更新布局(e);
		++i;
	}

	uint32 num = m_要拷贝的节点面板.size();
	for (uint32 i = 0; i < num; ++i) {
		for (auto& nodePanel : m_要拷贝的节点面板[i]->m_包含在多个视口的UI部件) {
			新节点面板[i]->m_同子集节点树关联UI部件 = ((C_节点面板*)nodePanel)->m_同子集节点树关联UI部件;
		}
		if(新节点面板[i]->m_同子集节点树关联UI部件) (*新节点面板[i]->m_同子集节点树关联UI部件).insert(新节点面板[i]);
	}
	
}


S_深拷贝节点剪切板::S_深拷贝节点剪切板(std::set<C_节点面板*>& nodes) {
	m_要拷贝的节点面板.reserve(nodes.size());

	for (auto& e : nodes) {
		m_要拷贝的节点面板.push_back(e->m_NodeData);
	}
}

void S_深拷贝节点剪切板::f_复制() {
	uint32 num = m_要拷贝的节点面板.size();
	m_节点中心坐标偏移.resize(num);
	vec2* 中心偏移 = m_节点中心坐标偏移.data();

	vec2 中心坐标 = {};

	for (uint32 i = 0; i < num; ++i) {
		中心坐标 += m_要拷贝的节点面板[i]->m_坐标;
	}
	中心坐标 /= num;

	for (uint32 i = 0; i < num; ++i) {
		中心偏移[i] = 中心坐标 - m_要拷贝的节点面板[i]->m_坐标;
	}
}

void S_深拷贝节点剪切板::f_粘贴() {
	auto 新节点面板 = f_NodeView_拷贝节点(g当前操作节点视口, m_要拷贝的节点面板, true);

	auto 鼠标坐标 = f_widget_get鼠标局部位置(C_Widget::g_鼠标.gPos, g当前操作节点视口);
	鼠标坐标 = f_NodeView_get变换前坐标(鼠标坐标, g当前操作节点视口->m_扩展视口属性);

	vec2* 中心偏移 = m_节点中心坐标偏移.data();

	uint32 i = 0;
	for (auto& e : 新节点面板) {
		e->m_NodeData->m_坐标 = 鼠标坐标 - 中心偏移[i];
		e->m_NodeData->m_坐标 -= f_NodePlane_get中心偏移(e);
		f_NodePlane_更新布局(e);
		++i;
	}

	uint32 num = m_要拷贝的节点面板.size();
	for (uint32 i = 0; i < num; ++i) {
		for (auto& nodePanel : m_要拷贝的节点面板[i]->m_包含在多个视口的UI部件) {
			新节点面板[i]->m_同子集节点树关联UI部件 = ((C_节点面板*)nodePanel)->m_同子集节点树关联UI部件;
		}
		if (新节点面板[i]->m_同子集节点树关联UI部件) (*新节点面板[i]->m_同子集节点树关联UI部件).insert(新节点面板[i]);
	}
}











void f_init节点视口(S_设备环境& ctx) {
	g计算环境 = f_创建计算环境(ctx);

	//g连接线交叉计算 = new S_连接线相交计算(*g计算环境);
	g剪线焦点 = f_buf_create板载缓存(*g计算环境, sizeof(vec2), E_板载缓存类型::e_SSBOd);
	vector<vec2> t(1024);
	f_buf_fill板载缓存(g剪线焦点, t, E_板载缓存类型::e_SSBOd);

	S_框架::gfu_更新选择节点 = f_NodeView_更新前选择节点;
	//f_init_物体节点创建_绑定视口回调(f_NodeView_添加节点面板到视口);

}

C_节点视口* f_NodeView_创建节点窗口(S_UI创建环境& ctx, C_节点树* tree) {
	auto* view = new C_节点视口(ctx, tree);
	f_widget_开启视口属性(view);

	view->m_属性栏 = f_创建属性边栏(ctx);
	view->m_属性栏->f_setSize({ 256,256 });
	


	view->m_扩展视口属性->m_缩放后边界 = view->f_get边界();
	view->m_扩展视口属性->m_偏移比例 = { 0.5f,0.5f };
	view->m_扩展视口属性->m_中心 = { 1,1 };
	view->m_扩展视口属性->m_比例 = { 1,1 };
	view->m_扩展视口属性->m_偏移比例 = { 0,0 };
	//view->mf_文件处理回调 = on_文件处理回调;

	f_设置当前操作节点视口(view);
	return view;
}







